my $vars = { target_type => 'attachment',
product_id => $self->bug->product_id,
component_id => $self->bug->component_id,
- attach_id => $self->id,
+ attach_id => $self->id,
active_or_has_flags => $self->bug_id };
return $self->{flag_types} = Bugzilla::Flag->_flag_types($vars);
$bug = ref $invocant ? $invocant->bug : $bug;
- $bug || ThrowCodeError('param_required',
+ $bug || ThrowCodeError('param_required',
{ function => "$invocant->create", param => 'bug' });
($user->can_see_bug($bug->id) && $user->can_edit_product($bug->product_id))
sub _check_content_type {
my ($invocant, $content_type, undef, $params) = @_;
-
+
my $is_patch = ref($invocant) ? $invocant->ispatch : $params->{ispatch};
$content_type = 'text/plain' if $is_patch;
$content_type = clean_text($content_type);
}
# Add in cvs context if we have the necessary info to do it
- if ($context ne 'patch' && Bugzilla->localconfig->{cvsbin}
- && Bugzilla->params->{'cvsroot_get'})
+ if ($context ne 'patch' && Bugzilla->localconfig->{cvsbin}
+ && Bugzilla->params->{'cvsroot_get'})
{
require Bugzilla::PatchReader::AddCVSContext;
# We need to set $cvsbin as global, because PatchReader::CVSClient
$vars->{'collapsed'} = $cgi->param('collapsed');
$vars->{'context'} = $context;
- $vars->{'do_context'} = Bugzilla->localconfig->{cvsbin}
+ $vars->{'do_context'} = Bugzilla->localconfig->{cvsbin}
&& Bugzilla->params->{'cvsroot_get'} && !$vars->{'newid'};
# Print everything out.
my $verifier = $self->{_verifier}->{successful};
$verifier ||= $self->{_verifier};
my $getter = $self->{_info_getter}->{successful};
- $getter = $self->{_info_getter}
+ $getter = $self->{_info_getter}
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
return $verifier->can_change_password &&
$getter->user_can_create_account;
}
}
elsif ($fail_code == AUTH_NODATA) {
- $self->{_info_getter}->fail_nodata($self)
+ $self->{_info_getter}->fail_nodata($self)
if $login_type == LOGIN_REQUIRED;
# If we're not LOGIN_REQUIRED, we just return the default user.
# the password was just wrong. (This makes it harder for a cracker
# to find account names by brute force)
elsif ($fail_code == AUTH_LOGINFAILED or $fail_code == AUTH_NO_SUCH_USER) {
- my $remaining_attempts = MAX_LOGIN_ATTEMPTS
+ my $remaining_attempts = MAX_LOGIN_ATTEMPTS
- ($result->{failure_count} || 0);
- ThrowUserError("invalid_username_or_password",
+ ThrowUserError("invalid_username_or_password",
{ remaining => $remaining_attempts });
}
# The account may be disabled
elsif ($fail_code == AUTH_LOCKOUT) {
my $attempts = $user->account_ip_login_failures;
- # We want to know when the account will be unlocked. This is
+ # We want to know when the account will be unlocked. This is
# determined by the 5th-from-last login failure (or more/less than
# 5th, if MAX_LOGIN_ATTEMPTS is not 5).
my $determiner = $attempts->[scalar(@$attempts) - MAX_LOGIN_ATTEMPTS];
- my $unlock_at = datetime_from($determiner->{login_time},
+ my $unlock_at = datetime_from($determiner->{login_time},
Bugzilla->local_timezone);
$unlock_at->add(minutes => LOGIN_LOCKOUT_INTERVAL);
# If we were *just* locked out, notify the maintainer about the
# lockout.
if ($result->{just_locked_out}) {
- # We're sending to the maintainer, who may be not a Bugzilla
+ # We're sending to the maintainer, who may be not a Bugzilla
# account, but just an email address. So we use the
# installation's default language for sending the email.
my $default_settings = Bugzilla::User::Setting::get_defaults();
}
$unlock_at->set_time_zone($user->timezone);
- ThrowUserError('account_locked',
+ ThrowUserError('account_locked',
{ ip_addr => $determiner->{ip_addr}, unlock_at => $unlock_at });
}
# If we get here, then we've run out of options, which shouldn't happen.
Handles authentication for Bugzilla users.
Authentication from Bugzilla involves two sets of modules. One set is
-used to obtain the username/password (from CGI, email, etc), and the
-other set uses this data to authenticate against the datasource
+used to obtain the username/password (from CGI, email, etc), and the
+other set uses this data to authenticate against the datasource
(the Bugzilla DB, LDAP, PAM, etc.).
-Modules for obtaining the username/password are subclasses of
+Modules for obtaining the username/password are subclasses of
L<Bugzilla::Auth::Login>, and modules for authenticating are subclasses
of L<Bugzilla::Auth::Verify>.
The hashref will also contain an C<error> element, which is the name
of an error from C<template/en/default/global/code-error.html> --
-the same type of error that would be thrown by
+the same type of error that would be thrown by
L<Bugzilla::Error::ThrowCodeError>.
The hashref *may* contain an element called C<details>, which is a hashref
-that should be passed to L<Bugzilla::Error::ThrowCodeError> as the
+that should be passed to L<Bugzilla::Error::ThrowCodeError> as the
various fields to be used in the error message.
=head2 C<AUTH_LOGINFAILED>
=head1 METHODS
-These are methods that can be called on a C<Bugzilla::Auth> object
+These are methods that can be called on a C<Bugzilla::Auth> object
itself.
=head2 Login
Bugzilla::Auth family, and how the C<login> function works.
A C<Bugzilla::Auth> object is essentially a collection of a few other
-objects: the "Info Getter," the "Verifier," and the "Persistence
+objects: the "Info Getter," the "Verifier," and the "Persistence
Mechanism."
They are used inside the C<login> function in the following order:
even without a username and password.)
Some Info Getters don't require any verification. For example, if we got
-the C<user_id> from a Cookie, we don't need to check the username and
+the C<user_id> from a Cookie, we don't need to check the username and
password.
If an Info Getter returns only a C<user_id> and no username/password,
=item C<extern_id>
-Some string that uniquely identifies the user in an external account
+Some string that uniquely identifies the user in an external account
source. If this C<extern_id> already exists in the database with
a different username, the username will be *changed* to be the
username specified in this C<$login_data>.
That is, let's my extern_id is C<mkanat>. I already have an account
in Bugzilla with the username of C<mkanat@foo.com>. But this time,
when I log in, I have an extern_id of C<mkanat> and a C<username>
-of C<mkanat@bar.org>. So now, Bugzilla will automatically change my
+of C<mkanat@bar.org>. So now, Bugzilla will automatically change my
username to C<mkanat@bar.org> instead of C<mkanat@foo.com>.
=item C<user>
-A L<Bugzilla::User> object representing the authenticated user.
+A L<Bugzilla::User> object representing the authenticated user.
Note that C<Bugzilla::Auth::login> may modify this object at various points.
=back
use constant requires_verification => 1;
use constant user_can_create_account => 0;
use constant is_automatic => 0;
-use constant extern_id_used => 0;
+use constant extern_id_used => 0;
sub new {
my ($class) = @_;
=head1 INFO METHODS
-These are methods that describe the capabilities of this
+These are methods that describe the capabilities of this
C<Bugzilla::Auth::Login> object. These are all no-parameter
methods that return either C<true> or C<false>.
print $cgi->header();
$template->process("account/auth/login.html.tmpl",
- { 'target' => $cgi->url(-relative=>1) })
+ { 'target' => $cgi->url(-relative=>1) })
|| ThrowTemplateError($template->error());
exit;
}
}
}
- # If we logged in successfully, then update the lastused
+ # If we logged in successfully, then update the lastused
# time on the login cookie
- $dbh->do("UPDATE logincookies SET lastused = NOW()
+ $dbh->do("UPDATE logincookies SET lastused = NOW()
WHERE cookie = ?", undef, $login_cookie);
return { user_id => $user_id };
}
return { failure => AUTH_NODATA } if !$env_email;
- return { username => $env_email, extern_id => $env_id,
+ return { username => $env_email, extern_id => $env_id,
realname => $env_realname };
}
}
$result = $object->get_login_info(@_);
$self->{successful} = $object;
-
+
# We only carry on down the stack if this method denied all knowledge.
last unless ($result->{failure}
- && ($result->{failure} eq AUTH_NODATA
+ && ($result->{failure} eq AUTH_NODATA
|| $result->{failure} eq AUTH_NO_SUCH_USER));
-
+
# If none of the methods succeed, it's undef.
$self->{successful} = undef;
}
$dbh->bz_start_transaction();
- my $login_cookie =
+ my $login_cookie =
Bugzilla::Token::GenerateUniqueToken('logincookies', 'cookie');
my $ip_addr = remote_ip();
if ( Bugzilla->params->{'rememberlogin'} eq 'on' ||
(Bugzilla->params->{'rememberlogin'} ne 'off' &&
$input_params->{'Bugzilla_remember'} &&
- $input_params->{'Bugzilla_remember'} eq 'on') )
+ $input_params->{'Bugzilla_remember'} eq 'on') )
{
# Not a session cookie, so set an infinite expiry
$cookieargs{'-expires'} = 'Fri, 01-Jan-2038 00:00:00 GMT';
}
if (Bugzilla->params->{'ssl_redirect'}) {
- # Make these cookies only be sent to us by the browser during
+ # Make these cookies only be sent to us by the browser during
# HTTPS sessions, if we're using SSL.
$cookieargs{'-secure'} = 1;
}
Bugzilla->audit(sprintf "successful login of %s from %s using \"%s\", authenticated by %s",
$user->login, $ip_addr, $cgi->user_agent // '', $auth_method);
}
-
+
return $login_cookie;
}
# passed only a username, and that username doesn't exist already.
if ($username && !$username_user_id && !$extern_user_id) {
validate_email_syntax($username)
- || return { failure => AUTH_ERROR,
+ || return { failure => AUTH_ERROR,
error => 'auth_invalid_email',
details => {addr => $username} };
# Usually we'd call validate_password, but external authentication
# XXX Theoretically this could fail with an error, but the fix for
# that is too involved to be done right now.
- my $user = Bugzilla::User->create({
- login_name => $username,
+ my $user = Bugzilla::User->create({
+ login_name => $username,
cryptpassword => $password,
realname => $real_name});
$username_user_id = $user->id;
=head1 DESCRIPTION
-Bugzilla::Auth::Verify provides the "Verifier" part of the Bugzilla
-login process. (For details, see the "STRUCTURE" section of
+Bugzilla::Auth::Verify provides the "Verifier" part of the Bugzilla
+login process. (For details, see the "STRUCTURE" section of
L<Bugzilla::Auth>.)
It is mostly an abstract class, requiring subclasses to implement
=head1 INFO METHODS
These are methods that describe the capabilities of this object.
-These are all no-parameter methods that return either C<true> or
+These are all no-parameter methods that return either C<true> or
C<false>.
=over 4
return { failure => AUTH_LOGINFAILED,
failure_count => scalar(@{ $user->account_ip_login_failures }),
};
- }
+ }
# Force the user to change their password if it does not meet the current
# criteria. This should usually only happen if the criteria has changed.
my $dn = $dn_result->shift_entry->dn;
- # Check the password.
+ # Check the password.
my $pw_result = $self->ldap->bind($dn, password => $params->{password});
return { failure => AUTH_LOGINFAILED } if $pw_result->code;
if (@emails > 1) {
# Cycle through the adresses and check if they're Bugzilla logins.
- # Use the first one that returns a valid id.
+ # Use the first one that returns a valid id.
foreach my $email (@emails) {
if ( login_to_id($email) ) {
$params->{bz_username} = $email;
$username = escape_filter_value($username);
return (base => Bugzilla->params->{"LDAPBaseDN"},
scope => "sub",
- filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"}
+ filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"}
. "=$username)"
. Bugzilla->params->{"LDAPfilter"} . ')');
}
my ($self) = @_;
my $bind_result;
if (Bugzilla->params->{"LDAPbinddn"}) {
- my ($LDAPbinddn,$LDAPbindpass) =
+ my ($LDAPbinddn,$LDAPbindpass) =
split(":",Bugzilla->params->{"LDAPbinddn"});
- $bind_result =
+ $bind_result =
$self->ldap->bind($LDAPbinddn, password => $LDAPbindpass);
}
else {
# We can't just do this in new(), because we're not allowed to throw any
# error from anywhere under Bugzilla::Auth::new -- otherwise we
# could create a situation where the admin couldn't get to editparams
-# to fix his mistake. (Because Bugzilla->login always calls
+# to fix his mistake. (Because Bugzilla->login always calls
# Bugzilla::Auth->new, and almost every page calls Bugzilla->login.)
sub ldap {
my ($self) = @_;
$self->{ldap} = new Net::LDAP(trim($_));
last if $self->{ldap};
}
- ThrowCodeError("ldap_connect_failed", { server => join(", ", @servers) })
+ ThrowCodeError("ldap_connect_failed", { server => join(", ", @servers) })
unless $self->{ldap};
# try to start TLS if needed
my $ref = RELATIONSHIPS;
# Clone it so that we don't modify the constant;
my %relationships = %$ref;
- Bugzilla::Hook::process('bugmail_relationships',
+ Bugzilla::Hook::process('bugmail_relationships',
{ relationships => \%relationships });
return %relationships;
}
if ($forced->{'owner'}) {
push (@assignees, Bugzilla::User->check($forced->{'owner'}));
}
-
+
if ($forced->{'qacontact'}) {
push (@qa_contacts, Bugzilla::User->check($forced->{'qacontact'}));
}
-
+
if ($forced->{'cc'}) {
foreach my $cc (@{$forced->{'cc'}}) {
push(@ccs, Bugzilla::User->check($cc));
###########################################################################
# Start of email filtering code
###########################################################################
-
+
# A user_id => roles hash to keep track of people.
my %recipients;
my %watching;
-
+
# Now we work out all the people involved with this bug, and note all of
# the relationships in a hash. The keys are userids, the values are an
# array of role constants.
-
+
# CCs
$recipients{$_->id}->{+REL_CC} = BIT_DIRECT foreach (@ccs);
-
+
# Reporter (there's only ever one)
$recipients{$bug->reporter->id}->{+REL_REPORTER} = BIT_DIRECT;
-
+
# QA Contact
if (Bugzilla->params->{'useqacontact'}) {
foreach (@qa_contacts) {
# Assignee
$recipients{$_->id}->{+REL_ASSIGNEE} = BIT_DIRECT foreach (@assignees);
- # The last relevant set of people are those who are being removed from
+ # The last relevant set of people are those who are being removed from
# their roles in this change. We get their names out of the diffs.
foreach my $change (@diffs) {
if ($change->{old}) {
foreach my $user_id (keys %recipients) {
$user_cache{$user_id} ||= new Bugzilla::User({ id => $user_id, cache => 1 });
}
-
+
Bugzilla::Hook::process('bugmail_recipients',
{ bug => $bug, recipients => \%recipients,
users => \%user_cache, diffs => \@diffs });
# that role.
foreach my $relationship (keys %{$recipients{$user_id}}) {
if ($user->wants_bug_mail($bug,
- $relationship,
+ $relationship,
$start ? \@diffs : [],
$comments,
$params->{dep_only},
$changer))
{
- $rels_which_want{$relationship} =
+ $rels_which_want{$relationship} =
$recipients{$user_id}->{$relationship};
}
}
{ function => 'add_see_also', param => '$value' });
}
- # We assume that the URL is an HTTP URL if there is no (something)://
+ # We assume that the URL is an HTTP URL if there is no (something)://
# in front.
if (!$uri->scheme) {
# This works better than setting $uri->scheme('http'), because
if ($ref_bug->id == $self_bug_id) {
ThrowUserError('see_also_self_reference');
}
-
+
my $product = $ref_bug->product_obj;
if (!Bugzilla->user->can_edit_product($product->id)) {
ThrowUserError("product_edit_denied",
sub _check_value {
my ($class, $uri) = @_;
-
+
$uri = $class->SUPER::_check_value($uri);
my $value = $uri->as_string;
{
$self->delete($param);
}
-
+
# Any "join" for custom search that's an AND can be removed, because
# that's the default.
if (($param =~ /^j\d+$/ || $param eq 'j_top')
# "Reuse same sort as last time" is actually the default, so we don't
# need it in the URL.
- if ($self->param('order')
+ if ($self->param('order')
&& $self->param('order') eq 'Reuse same sort as last time')
{
$self->delete('order');
# list_id is added in buglist.cgi after calling clean_search_url,
# and doesn't need to be saved in saved searches.
- $self->delete('list_id');
+ $self->delete('list_id');
# And now finally, if query_format is our only parameter, that
# really means we have no parameters, so we should delete query_format.
# Have to add the cookies in.
sub multipart_start {
my $self = shift;
-
+
my %args = @_;
# CGI.pm::multipart_start doesn't honour its own charset information, so
# and add the specified one
$args{-type} .= '; charset=' . $self->charset();
}
-
+
my $headers = $self->SUPER::multipart_start(%args);
# Eliminate the one extra CRLF at the end.
$headers =~ s/$CGI::CRLF$//;
# Add Strict-Transport-Security (STS) header if this response
# is over SSL and the strict_transport_security param is turned on.
if ($self->https && !$self->url_is_attachment_base
- && Bugzilla->params->{'strict_transport_security'} ne 'off')
+ && Bugzilla->params->{'strict_transport_security'} ne 'off')
{
my $sts_opts = 'max-age=' . MAX_STS_AGE;
- if (Bugzilla->params->{'strict_transport_security'}
+ if (Bugzilla->params->{'strict_transport_security'}
eq 'include_subdomains')
{
$sts_opts .= '; includeSubDomains';
# When we are just requesting the value of a parameter...
if (scalar(@_) == 1) {
- my @result = $self->SUPER::param(@_);
+ my @result = $self->SUPER::param(@_);
- # Also look at the URL parameters, after we look at the POST
+ # Also look at the URL parameters, after we look at the POST
# parameters. This is to allow things like login-form submissions
# with URL parameters in the form's "target" attribute.
if (!scalar(@result)
# And for various other functions in CGI.pm, we need to correctly
# return the URL parameters in addition to the POST parameters when
# asked for the list of parameters.
- elsif (!scalar(@_) && $self->request_method
- && $self->request_method eq 'POST')
+ elsif (!scalar(@_) && $self->request_method
+ && $self->request_method eq 'POST')
{
my @post_params = $self->SUPER::param;
my @url_params = $self->url_param;
sub should_set {
my ($self, $param) = @_;
- my $set = (defined $self->param($param)
+ my $set = (defined $self->param($param)
or defined $self->param("defined_$param"))
? 1 : 0;
return $set;
$self->clean_search_url();
- # Make sure we still have params still after cleaning otherwise we
+ # Make sure we still have params still after cleaning otherwise we
# do not want to store a list_id for an empty search.
if ($user->id && $self->param) {
# Insert a placeholder Bugzilla::Search::Recent, so that we know what
# in the WebService, and WebService clients usually handle this
# correctly.
$self->delete('POSTDATA');
- my $url = $sslbase . $self->url('-path_info' => 1, '-query' => 1,
+ my $url = $sslbase . $self->url('-path_info' => 1, '-query' => 1,
'-relative' => 1);
# XML-RPC clients (SOAP::Lite at least) require a 301 to redirect properly
# Vars TIEHASH Interface #
##########################
-# Fix the TIEHASH interface (scalar $cgi->Vars) to return and accept
+# Fix the TIEHASH interface (scalar $cgi->Vars) to return and accept
# arrayrefs.
sub STORE {
my $self = shift;
return \@result;
}
-# For the Vars TIEHASH interface: the normal CGI.pm DELETE doesn't return
+# For the Vars TIEHASH interface: the normal CGI.pm DELETE doesn't return
# the value deleted, but Perl's "delete" expects that value.
sub DELETE {
my ($self, $param) = @_;
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
-
+
# Create a ref to an empty hash and bless it
my $self = {};
bless($self, $class);
if ($#_ == 0) {
# Construct from a CGI object.
$self->init($_[0]);
- }
+ }
else {
die("CGI object not passed in - invalid number of args \($#_\)($_)");
}
my $self = shift;
my $cgi = shift;
- # The data structure is a list of lists (lines) of Series objects.
+ # The data structure is a list of lists (lines) of Series objects.
# There is a separate list for the labels.
#
# The URL encoding is:
# line0=67&line0=73&line1=81&line2=67...
# &label0=B+/+R+/+CONFIRMED&label1=...
- # &select0=1&select3=1...
+ # &select0=1&select3=1...
# &cumulate=1&datefrom=2002-02-03&dateto=2002-04-04&ctype=html...
- # >=1&labelgt=Grand+Total
+ # >=1&labelgt=Grand+Total
foreach my $param ($cgi->param()) {
# Store all the lines
if ($param =~ /^line(\d+)$/) {
foreach my $series_id ($cgi->param($param)) {
- detaint_natural($series_id)
+ detaint_natural($series_id)
|| ThrowCodeError("invalid_series_id");
my $series = new Bugzilla::Series($series_id);
push(@{$self->{'lines'}[$1]}, $series) if $series;
# Store all the labels
if ($param =~ /^label(\d+)$/) {
$self->{'labels'}[$1] = $cgi->param($param);
- }
+ }
}
-
+
# Store the miscellaneous metadata
$self->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0;
$self->{'gt'} = $cgi->param('gt') ? 1 : 0;
$self->{'labelgt'} = $cgi->param('labelgt');
$self->{'datefrom'} = $cgi->param('datefrom');
$self->{'dateto'} = $cgi->param('dateto');
-
+
# If we are cumulating, a grand total makes no sense
$self->{'gt'} = 0 if $self->{'cumulate'};
-
+
# Make sure the dates are ones we are able to interpret
foreach my $date ('datefrom', 'dateto') {
if ($self->{$date}) {
- $self->{$date} = str2time($self->{$date})
+ $self->{$date} = str2time($self->{$date})
|| ThrowUserError("illegal_date", { date => $self->{$date}});
}
}
# datefrom can't be after dateto
- if ($self->{'datefrom'} && $self->{'dateto'} &&
- $self->{'datefrom'} > $self->{'dateto'})
+ if ($self->{'datefrom'} && $self->{'dateto'} &&
+ $self->{'datefrom'} > $self->{'dateto'})
{
ThrowUserError('misarranged_dates', { 'datefrom' => scalar $cgi->param('datefrom'),
'dateto' => scalar $cgi->param('dateto') });
# Get the current size of the series; required for adding Grand Total later
my $current_size = scalar($self->getSeriesIDs());
-
+
# Count the number of added series
my $added = 0;
# Create new Series and push them on to the list of lines.
$added++;
}
}
-
+
# If we are going from < 2 to >= 2 series, add the Grand Total line.
if (!$self->{'gt'}) {
if ($current_size < 2 &&
- $current_size + $added >= 2)
+ $current_size + $added >= 2)
{
$self->{'gt'} = 1;
}
sub remove {
my $self = shift;
my @line_ids = @_;
-
+
foreach my $line_id (@line_ids) {
if ($line_id == 65536) {
# Magic value - delete Grand Total.
$self->{'gt'} = 0;
- }
+ }
else {
delete($self->{'lines'}->[$line_id]);
delete($self->{'labels'}->[$line_id]);
sub sum {
my $self = shift;
my @line_ids = @_;
-
+
# We can't add the Grand Total to things.
@line_ids = grep(!/^65536$/, @line_ids);
-
+
# We can't add less than two things.
return if scalar(@line_ids) < 2;
-
+
my @series;
my $label = "";
my $biggestlength = 0;
-
+
# We rescue the Series objects of all the series involved in the sum.
foreach my $line_id (@line_ids) {
my @line = @{$self->{'lines'}->[$line_id]};
-
+
foreach my $series (@line) {
push(@series, $series);
}
-
+
# We keep the label that labels the line with the most series.
if (scalar(@line) > $biggestlength) {
$biggestlength = scalar(@line);
# Work out the date boundaries for our data.
my $dbh = Bugzilla->dbh;
-
+
# The date used is the one given if it's in a sensible range; otherwise,
# it's the earliest or latest date in the database as appropriate.
- my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " .
+ my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " .
"FROM series_data " .
"WHERE series_id IN ($series_ids)");
$datefrom = str2time($datefrom);
$datefrom = $self->{'datefrom'};
}
- my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " .
+ my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " .
"FROM series_data " .
"WHERE series_id IN ($series_ids)");
- $dateto = str2time($dateto);
+ $dateto = str2time($dateto);
if ($self->{'dateto'} && $self->{'dateto'} < $dateto) {
$dateto = $self->{'dateto'};
if ($dateto) {
$query .= " AND series_date <= ?";
}
-
+
my $sth = $dbh->prepare($query);
my $gt_index = $self->{'gt'} ? scalar(@{$self->{'lines'}}) : undef;
my @datediff_total;
- foreach my $line (@{$self->{'lines'}}) {
+ foreach my $line (@{$self->{'lines'}}) {
# Even if we end up with no data, we need an empty arrayref to prevent
# errors in the PNG-generating code
$data[$line_index] = [];
} while ($self->{'y_max_value'} % (8*(10**($mask_length-1))) != 0);
}
-
+
# Add the x-axis labels into the data structure
my $date_progression = generateDateProgression($datefrom, $dateto);
unshift(@data, $date_progression);
my %cats;
my $grouplist = Bugzilla->user->groups_as_string;
-
+
# Get all visible series
my $dbh = Bugzilla->dbh;
my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " .
# Make sure we've read in our data
my $data = $self->data;
-
+
require Data::Dumper;
print "<pre>Bugzilla::Chart object:\n";
print html_quote(Data::Dumper::Dumper($self));
return $self->{attachment};
}
-sub author {
+sub author {
my $self = shift;
return $self->{'author'}
||= new Bugzilla::User({ id => $self->{'who'}, cache => 1 });
my $template = Bugzilla->template_inner;
my $body;
if ($self->type) {
- $template->process("bug/format_comment.txt.tmpl",
+ $template->process("bug/format_comment.txt.tmpl",
{ comment => $self, %$params }, \$body)
|| ThrowTemplateError($template->error());
$body =~ s/^X//;
if (!defined $extra_data) {
ThrowCodeError('comment_extra_data_required', { type => $type });
}
- elsif ($type == CMT_ATTACHMENT_CREATED
- or $type == CMT_ATTACHMENT_UPDATED)
+ elsif ($type == CMT_ATTACHMENT_CREATED
+ or $type == CMT_ATTACHMENT_UPDATED)
{
- my $attachment = Bugzilla::Attachment->check({
+ my $attachment = Bugzilla::Attachment->check({
id => $extra_data });
$extra_data = $attachment->id;
}
else {
my $original = $extra_data;
- detaint_natural($extra_data)
+ detaint_natural($extra_data)
or ThrowCodeError('comment_extra_data_not_numeric',
{ type => $type, extra_data => $original });
}
# Make sure the user can comment
my $privs;
$bug->check_can_change_field('longdesc', 0, 1, \$privs)
- || ThrowUserError('illegal_change',
+ || ThrowUserError('illegal_change',
{ field => 'longdesc', privs => $privs });
return $bug;
}
=head1 NAME
-Bugzilla::Comment - A Comment for a given bug
+Bugzilla::Comment - A Comment for a given bug
=head1 SYNOPSIS
Bugzilla::Comment represents a comment attached to a bug.
-This implements all standard C<Bugzilla::Object> methods. See
+This implements all standard C<Bugzilla::Object> methods. See
L<Bugzilla::Object> for more details.
=head2 Accessors
sub _check_product {
my ($invocant, $product) = @_;
- $product || ThrowCodeError('param_required',
+ $product || ThrowCodeError('param_required',
{ function => "$invocant->create", param => 'product' });
return Bugzilla->user->check_can_admin_product($product->name);
}
if (!defined $self->{'flag_types'}) {
my $flagtypes = Bugzilla::FlagType::match({ product_id => $self->product_id,
- component_id => $self->id,
+ component_id => $self->id,
%$params });
$self->{'flag_types'} = {};
}
# This hook is also called in editparams.cgi. This call here is required
# to make SetParam work.
- Bugzilla::Hook::process('config_modify_panels',
+ Bugzilla::Hook::process('config_modify_panels',
{ panels => \%hook_panels });
foreach my $panel (keys %hook_panels) {
$param_panels->{$module} = "Bugzilla::Config::$module" unless $module eq 'Common';
}
# Now check for any hooked params
- Bugzilla::Hook::process('config_add_panels',
+ Bugzilla::Hook::process('config_add_panels',
{ panel_modules => $param_panels });
return $param_panels;
}
# Change from old product groups to controls for group_control_map
# 2002-10-14 bug 147275 bugreport@peshkin.net
- if (exists $param->{'usebuggroups'} &&
- !exists $param->{'makeproductgroups'})
+ if (exists $param->{'usebuggroups'} &&
+ !exists $param->{'makeproductgroups'})
{
$new_params{'makeproductgroups'} = $param->{'usebuggroups'};
}
}
# set verify method to whatever loginmethod was
- if (exists $param->{'loginmethod'}
- && !exists $param->{'user_verify_class'})
+ if (exists $param->{'loginmethod'}
+ && !exists $param->{'user_verify_class'})
{
$new_params{'user_verify_class'} = $param->{'loginmethod'};
}
# Remove quip-display control from parameters
# and give it to users via User Settings (Bug 41972)
- if ( exists $param->{'enablequips'}
- && !exists $param->{'quip_list_entry_control'})
+ if ( exists $param->{'enablequips'}
+ && !exists $param->{'quip_list_entry_control'})
{
my $new_value;
($param->{'enablequips'} eq 'on') && do {$new_value = 'open';};
sub check_user_verify_class {
# doeditparams traverses the list of params, and for each one it checks,
- # then updates. This means that if one param checker wants to look at
- # other params, it must be below that other one. So you can't have two
+ # then updates. This means that if one param checker wants to look at
+ # other params, it must be below that other one. So you can't have two
# params mutually dependent on each other.
# This means that if someone clears the LDAP config params after setting
# the login method as LDAP, we won't notice, but all logins will fail.
return "LDAP support is not available. Run checksetup.pl"
. " for more details";
}
- return "LDAP servername (LDAPserver) is missing"
+ return "LDAP servername (LDAPserver) is missing"
if !$params->{"LDAPserver"};
return "LDAPBaseDN is empty" if !$params->{"LDAPBaseDN"};
}
return $check if $check;
my $mailer = shift;
if ($mailer eq 'sendmail' and ON_WINDOWS) {
- # look for sendmail.exe
+ # look for sendmail.exe
return "Failed to locate " . SENDMAIL_EXE
unless -e SENDMAIL_EXE;
}
# }
#
# Here, 'b' is the default option, and 'a' and 'c' are other possible
-# options, but only one at a time!
+# options, but only one at a time!
#
# &check_multi should always be used as the param verification function
# for list (single and multiple) parameter types.
=head1 DESCRIPTION
-All parameter checking functions are called with two parameters: the value to
+All parameter checking functions are called with two parameters: the value to
check, and a hash with the details of the param (type, default etc.) as defined
in the relevant F<Bugzilla::Config::*> package.
default => 'admin',
checker => \&check_group
},
-
+
{
name => 'usevisibilitygroups',
type => 'b',
default => 0
- },
-
+ },
+
{
name => 'strict_isolation',
type => 'b',
},
{
- name => 'ajax_user_autocompletion',
- type => 'b',
- default => '1',
+ name => 'ajax_user_autocompletion',
+ type => 'b',
+ default => '1',
},
{
use constant INDEX_DROPS_REQUIRE_FK_DROPS => 1;
#####################################################################
-# Overridden Superclass Methods
+# Overridden Superclass Methods
#####################################################################
sub quote {
sub connect_main {
my $lc = Bugzilla->localconfig;
- return _connect(Bugzilla->localconfig);
+ return _connect(Bugzilla->localconfig);
}
sub _connect {
die "There was an error connecting to $sql_server:\n\n",
" $error\n\n", _bz_connect_error_reasons(), "\n";
}
- return $dbh;
+ return $dbh;
}
# Just a helper because we have to re-use this text.
server configuration or the database access rights. Read the Bugzilla
Guide in the doc directory. The section about database configuration
should help.
-* Your password for the '$lc->{db_user}' user, specified in \$db_pass, is
+* Your password for the '$lc->{db_user}' user, specified in \$db_pass, is
incorrect, in '$lc_file'.
* There is a subtle problem with Perl, DBI, or $server. Make
sure all settings in '$lc_file' are correct. If all else fails, set
my $expression = "GROUP BY $needed_columns";
$expression .= ", " . $optional_columns if $optional_columns;
-
+
return $expression;
}
sub sql_string_concat {
my ($self, @params) = @_;
-
+
return '(' . join(' || ', @params) . ')';
}
sub bz_last_key {
my ($self, $table, $column) = @_;
- return $self->last_insert_id(Bugzilla->localconfig->{db_name}, undef,
+ return $self->last_insert_id(Bugzilla->localconfig->{db_name}, undef,
$table, $column);
}
eval { $self->do("SELECT " . $self->sql_regexp($self->quote("a"), $pattern, 1)) };
- $@ && ThrowUserError('illegal_regexp',
- { value => $pattern, dberror => $self->errstr });
+ $@ && ThrowUserError('illegal_regexp',
+ { value => $pattern, dberror => $self->errstr });
}
#####################################################################
# If we haven't ever stored a serialized schema,
# set up the bz_schema table and store it.
$self->_bz_init_schema_storage();
-
+
# We don't use bz_table_list here, because that uses _bz_real_schema.
# We actually want the table list from the ABSTRACT_SCHEMA in
# Bugzilla::DB::Schema.
}
sub bz_populate_enum_tables {
- my ($self) = @_;
+ my ($self) = @_;
my $any_severities = $self->selectrow_array(
'SELECT 1 FROM bug_severity ' . $self->sql_limit(1));
# by enabling an extension pre-4.2, disabling it for
# the 4.2 upgrade, and then re-enabling it later.
unless ($fk && $fk->{created}) {
- my $standard_def =
+ my $standard_def =
$self->_bz_schema->get_column_abstract($table, $column);
if (exists $standard_def->{REFERENCES}) {
$fk = dclone($standard_def->{REFERENCES});
# no DEFAULT statement, unless you have an init_value.
# SERIAL types are an exception, though, because they can
# auto-populate.
- if ( $new_def->{NOTNULL} && !exists $new_def->{DEFAULT}
+ if ( $new_def->{NOTNULL} && !exists $new_def->{DEFAULT}
&& !defined $init_value && $new_def->{TYPE} !~ /SERIAL/)
{
ThrowCodeError('column_not_null_without_default',
my $new_fk = $column_fks->{$column};
$self->_check_references($table, $column, $new_fk);
$add_these{$column} = $new_fk;
- if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE
- and !$options->{silently})
+ if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE
+ and !$options->{silently})
{
print get_text('install_fk_add',
- { table => $table, column => $column,
+ { table => $table, column => $column,
fk => $new_fk }), "\n";
}
}
if (!$self->_bz_schema->columns_equal($current_def, $new_def)) {
# You can't change a column to be NOT NULL if you have no DEFAULT
- # and no value for $set_nulls_to, if there are any NULL values
+ # and no value for $set_nulls_to, if there are any NULL values
# in that column.
- if ($new_def->{NOTNULL} &&
+ if ($new_def->{NOTNULL} &&
!exists $new_def->{DEFAULT} && !defined $set_nulls_to)
{
# Check for NULLs
my $any_nulls = $self->selectrow_array(
"SELECT 1 FROM $table WHERE $name IS NULL");
- ThrowCodeError('column_not_null_no_default_alter',
+ ThrowCodeError('column_not_null_no_default_alter',
{ name => "$table.$name" }) if ($any_nulls);
}
# Preserve foreign key definitions in the Schema object when altering
$fields{$col}->{REFERENCES}->{created} =
$self->_bz_real_schema->FK_ON_CREATE;
}
-
+
$self->_bz_real_schema->add_table($name, $table_def);
$self->_bz_store_real_schema;
}
# _bz_init_schema_storage. Used when you don't
# yet have a Schema object but you need to
# add a table, for some reason.
-# Params: $name - The name of the table you're creating.
-# The definition for the table is pulled from
+# Params: $name - The name of the table you're creating.
+# The definition for the table is pulled from
# _bz_schema.
# Returns: nothing
#
sub bz_add_field_tables {
my ($self, $field) = @_;
-
+
$self->_bz_add_field_table($field->name,
$self->_bz_schema->FIELD_TABLE_SCHEMA, $field->type);
if ($field->type == FIELD_TYPE_MULTI_SELECT) {
$self->_bz_add_field_table($ms_table,
$self->_bz_schema->MULTI_SELECT_VALUE_TABLE);
- $self->bz_add_fks($ms_table,
+ $self->bz_add_fks($ms_table,
{ bug_id => {TABLE => 'bugs', COLUMN => 'bug_id',
DELETE => 'CASCADE'},
if ($current_def) {
my @statements = $self->_bz_real_schema->get_drop_column_ddl(
$table, $column);
- print get_text('install_column_drop',
+ print get_text('install_column_drop',
{ table => $table, column => $column }) . "\n"
if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
foreach my $sql (@statements) {
print get_text('install_fk_drop',
{ table => $table, column => $column, fk => $fk_def })
. "\n" if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
- my @statements =
+ my @statements =
$self->_bz_real_schema->get_drop_fk_sql($table, $column, $fk_def);
foreach my $sql (@statements) {
# Because this is a deletion, we don't want to die hard if
my @columns = $self->bz_table_columns($check_table);
foreach my $check_column (@columns) {
my $fk = $self->bz_fk_info($check_table, $check_column);
- if ($fk
+ if ($fk
and (($fk->{TABLE} eq $table and $fk->{COLUMN} eq $column)
or ($check_column eq $column and $check_table eq $table)))
{
}
$self->bz_drop_index_raw($table, $name);
$self->_bz_real_schema->delete_index($table, $name);
- $self->_bz_store_real_schema;
+ $self->_bz_store_real_schema;
}
}
# Drops an index from the database
# without updating any Schema object. Generally
# should only be called by bz_drop_index.
-# Used when either: (1) You don't yet have a Schema
+# Used when either: (1) You don't yet have a Schema
# object but you need to drop an index, for some reason.
# (2) You need to drop an index that somehow got into the
# database but doesn't exist in Schema.
if ($old_col_exists) {
my $already_renamed = $self->bz_column_info($table, $new_name);
ThrowCodeError('db_rename_conflict',
- { old => "$table.$old_name",
+ { old => "$table.$old_name",
new => "$table.$new_name" }) if $already_renamed;
my @statements = $self->_bz_real_schema->get_rename_column_ddl(
$table, $old_name, $new_name);
- print get_text('install_column_rename',
+ print get_text('install_column_rename',
{ old => "$table.$old_name", new => "$table.$new_name" })
. "\n" if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
}
my @sql = $self->_bz_real_schema->get_rename_table_sql($old_name, $new_name);
- print get_text('install_table_rename',
+ print get_text('install_table_rename',
{ old => $old_name, new => $new_name }) . "\n"
if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
$self->do($_) foreach @sql;
# Different DBs have different defaults for their isolation
# level, so we just set it here manually.
if ($self->ISOLATION_LEVEL) {
- $self->do('SET TRANSACTION ISOLATION LEVEL '
+ $self->do('SET TRANSACTION ISOLATION LEVEL '
. $self->ISOLATION_LEVEL);
}
$self->{private_bz_transaction_count} = 1;
sub bz_commit_transaction {
my ($self) = @_;
-
+
if ($self->{private_bz_transaction_count} > 1) {
$self->{private_bz_transaction_count}--;
} elsif ($self->bz_in_transaction) {
sub db_new {
my ($class, $params) = @_;
- my ($dsn, $user, $pass, $override_attrs) =
+ my ($dsn, $user, $pass, $override_attrs) =
@$params{qw(dsn user pass attrs)};
# set up default attributes used to connect to the database
ShowErrorStatement => 1,
HandleError => \&_handle_error,
TaintIn => 1,
- FetchHashKeyName => 'NAME',
+ FetchHashKeyName => 'NAME',
# Note: NAME_lc causes crash on ActiveState Perl
# 5.8.4 (see Bug 253696)
# XXX - This will likely cause problems in DB
. " Is your database installed and up and running?\n Do you have"
. " the correct username and password selected in localconfig?\n\n";
- # RaiseError was only set to 0 so that we could catch the
+ # RaiseError was only set to 0 so that we could catch the
# above "die" condition.
$self->{RaiseError} = 1;
my $table_size;
eval {
- $table_size =
+ $table_size =
$self->selectrow_array("SELECT COUNT(*) FROM bz_schema");
};
$self->_bz_schema->get_table_abstract('bz_schema'));
$self->_bz_store_real_schema;
}
- }
+ }
# Sanity check
elsif ($table_size > 1) {
# We tell them to delete the newer one. Better to have checksetup
my $update_schema = $self->{private_real_schema};
my $store_me = $update_schema->serialize_abstract();
my $schema_version = $update_schema->SCHEMA_VERSION;
- my $sth = $self->prepare("UPDATE bz_schema
+ my $sth = $self->prepare("UPDATE bz_schema
SET schema_data = ?, version = ?");
$sth->bind_param(1, $store_me, $self->BLOB_TYPE);
$sth->bind_param(2, $schema_version);
# We also can't use the words "table" or "foreign" because those are
# reserved words.
my $bad_values = $self->selectcol_arrayref(
- "SELECT DISTINCT tabl.$column
+ "SELECT DISTINCT tabl.$column
FROM $table AS tabl LEFT JOIN $foreign_table AS forn
ON tabl.$column = forn.$foreign_column
WHERE forn.$foreign_column IS NULL
if (@$bad_values) {
my $delete_action = $fk->{DELETE} || '';
if ($delete_action eq 'CASCADE') {
- $self->do("DELETE FROM $table WHERE $column IN ("
+ $self->do("DELETE FROM $table WHERE $column IN ("
. join(',', ('?') x @$bad_values) . ")",
undef, @$bad_values);
if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
print "\n", get_text('install_fk_invalid_fixed',
{ table => $table, column => $column,
- foreign_table => $foreign_table,
+ foreign_table => $foreign_table,
foreign_column => $foreign_column,
'values' => $bad_values, action => 'null' }), "\n";
}
=item C<BLOB_TYPE>
-The C<\%attr> argument that must be passed to bind_param in order to
+The C<\%attr> argument that must be passed to bind_param in order to
correctly escape a C<LONGBLOB> type.
=item C<ISOLATION_LEVEL>
-The argument that this database should send to
+The argument that this database should send to
C<SET TRANSACTION ISOLATION LEVEL> when starting a transaction. If you
override this in a subclass, the isolation level you choose should
be as strict as or more strict than the default isolation level defined in
=over
=item C<$no_db_name> (optional) - If true, connect to the database
-server, but don't connect to a specific database. This is only used
-when creating a database. After you create the database, you should
-re-create a new Bugzilla::DB object without using this parameter.
+server, but don't connect to a specific database. This is only used
+when creating a database. After you create the database, you should
+re-create a new Bugzilla::DB object without using this parameter.
=back
=item B<Description>
-Checks to make sure that you have the correct DBD and database version
-installed for the database that Bugzilla will be using. Prints a message
+Checks to make sure that you have the correct DBD and database version
+installed for the database that Bugzilla will be using. Prints a message
and exits if you don't pass the requirements.
-If C<$db_check> is false (from F<localconfig>), we won't check the
+If C<$db_check> is false (from F<localconfig>), we won't check the
database version.
=item B<Params>
=over
-=item C<$output> - C<true> if the function should display informational
+=item C<$output> - C<true> if the function should display informational
output about what it's doing, such as versions found.
=back
=item B<Description>
-Creates an empty database with the name C<$db_name>, if that database
-doesn't already exist. Prints an error message and exits if we can't
+Creates an empty database with the name C<$db_name>, if that database
+doesn't already exist. Prints an error message and exits if we can't
create the database.
=item B<Params> (none)
=item B<Description>
-Internal function, creates and returns a new, connected instance of the
+Internal function, creates and returns a new, connected instance of the
correct DB class. This routine C<die>s if no driver is specified.
=item B<Params>
=item C<_handle_error>
-Function passed to the DBI::connect call for error handling. It shortens the
+Function passed to the DBI::connect call for error handling. It shortens the
error for printing.
=item C<import>
Overrides the standard import method to check that derived class
-implements all required abstract methods. Also calls original implementation
+implements all required abstract methods. Also calls original implementation
in its super class.
=back
=item B<Description>
-Constructor. Abstract method, should be overridden by database specific
+Constructor. Abstract method, should be overridden by database specific
code.
=item B<Params>
-=over
+=over
=item C<$user> - username used to log in to the database
=item B<Returns>
-Formatted SQL for negative regular expression search (e.g. NOT REGEXP)
+Formatted SQL for negative regular expression search (e.g. NOT REGEXP)
(scalar)
=back
C<integer> The time interval you're adding or subtracting (e.g. C<30>)
-=item C<$units>
+=item C<$units>
C<string> the units the interval is in (e.g. 'MINUTE')
expression that can be used for ranking.
There is a ANSI SQL version of this method implemented using LIKE operator,
-but it's not a real full text search. DB specific modules should override
-this, as this generic implementation will be always much slower. This
-generic implementation returns 'relevance' as 0 for no match, or 1 for a
+but it's not a real full text search. DB specific modules should override
+this, as this generic implementation will be always much slower. This
+generic implementation returns 'relevance' as 0 for no match, or 1 for a
match.
=item B<Params>
=item C<$right> - What should be on the right-hand-side of the operation.
-=item C<$op> (optional) - What the operation is. Should be a valid ANSI
-SQL comparison operator, such as C<=>, C<E<lt>>, C<LIKE>, etc. Defaults
+=item C<$op> (optional) - What the operation is. Should be a valid ANSI
+SQL comparison operator, such as C<=>, C<E<lt>>, C<LIKE>, etc. Defaults
to C<=> if not specified.
=back
=item B<Description>
-Returns SQL syntax "preparing" a string or text column for case-insensitive
+Returns SQL syntax "preparing" a string or text column for case-insensitive
comparison.
=item B<Params>
=item B<Description>
-Returns SQL syntax for the C<IN ()> operator.
+Returns SQL syntax for the C<IN ()> operator.
Only necessary where an C<IN> clause can have more than 1000 items.
=head1 IMPLEMENTED METHODS
These methods are implemented in Bugzilla::DB, and only need
-to be implemented in subclasses if you need to override them for
+to be implemented in subclasses if you need to override them for
database-compatibility reasons.
=head2 General Information Methods
=item B<Description>
-For an upgrade or an initial installation, populates the tables that hold
-the legal values for the old "enum" fields: C<bug_severity>,
+For an upgrade or an initial installation, populates the tables that hold
+the legal values for the old "enum" fields: C<bug_severity>,
C<resolution>, etc. Prints out information if it inserts anything into the
DB.
=item B<Description>
-Adds a new column to a table in the database. Prints out a brief statement
-that it did so, to stdout. Note that you cannot add a NOT NULL column that
+Adds a new column to a table in the database. Prints out a brief statement
+that it did so, to stdout. Note that you cannot add a NOT NULL column that
has no default -- the database won't know what to set all the NULL
values to.
=item B<Description>
-Adds a new index to a table in the database. Prints out a brief statement
+Adds a new index to a table in the database. Prints out a brief statement
that it did so, to stdout. If the index already exists, we will do nothing.
=item B<Params>
=item C<$name> - A name for the new index.
-=item C<$definition> - An abstract index definition. Either a hashref
+=item C<$definition> - An abstract index definition. Either a hashref
or an arrayref.
=back
=item B<Description>
-Creates a new table in the database, based on the definition for that
+Creates a new table in the database, based on the definition for that
table in the abstract schema.
-Note that unlike the other 'add' functions, this does not take a
+Note that unlike the other 'add' functions, this does not take a
definition, but always creates the table as it exists in
L<Bugzilla::DB::Schema/ABSTRACT_SCHEMA>.
-If a table with that name already exists, then this function returns
+If a table with that name already exists, then this function returns
silently.
=item B<Params>
=item B<Description>
-Removes an index from the database. Prints out a brief statement that it
+Removes an index from the database. Prints out a brief statement that it
did so, to stdout. If the index doesn't exist, we do nothing.
=item B<Params>
=item B<Description>
-Drops a table from the database. If the table doesn't exist, we just
+Drops a table from the database. If the table doesn't exist, we just
return silently.
=item B<Params>
=item B<Description>
-Changes the data type of a column in a table. Prints out the changes
-being made to stdout. If the new type is the same as the old type,
+Changes the data type of a column in a table. Prints out the changes
+being made to stdout. If the new type is the same as the old type,
the function returns without changing anything.
=item B<Params>
=item C<$name> - the name of the column you want to change
-=item C<\%new_def> - An abstract column definition for the new
+=item C<\%new_def> - An abstract column definition for the new
data type of the columm
=item C<$set_nulls_to> (Optional) - If you are changing the column
-to be NOT NULL, you probably also want to set any existing NULL columns
-to a particular value. Specify that value here. B<NOTE>: The value should
+to be NOT NULL, you probably also want to set any existing NULL columns
+to a particular value. Specify that value here. B<NOTE>: The value should
not already be SQL-quoted.
=back
=item B<Description>
-Removes a column from a database table. If the column doesn't exist, we
-return without doing anything. If we do anything, we print a short
+Removes a column from a database table. If the column doesn't exist, we
+return without doing anything. If we do anything, we print a short
message to C<stdout> about the change.
=item B<Params>
=item B<Description>
-Renames a column in a database table. If the C<$old_name> column
-doesn't exist, we return without doing anything. If C<$old_name>
+Renames a column in a database table. If the C<$old_name> column
+doesn't exist, we return without doing anything. If C<$old_name>
and C<$new_name> both already exist in the table specified, we fail.
=item B<Params>
=over
-=item C<$table> - The name of the table containing the column
+=item C<$table> - The name of the table containing the column
that you want to rename
=item C<$old_name> - The current name of the column that you want to rename
Renames a table in the database. Does nothing if the table doesn't exist.
-Throws an error if the old table exists and there is already a table
+Throws an error if the old table exists and there is already a table
with the new name.
=item B<Params>
=head2 Schema Information Methods
These methods return information about the current Bugzilla database
-schema, as it currently exists on the disk.
+schema, as it currently exists on the disk.
Where a parameter says "Abstract index/column definition", it returns/takes
information in the formats defined for indexes and columns for
=item B<Returns>
-An abstract column definition for that column. If the table or column
+An abstract column definition for that column. If the table or column
does not exist, we return C<undef>.
=back
=item B<Returns>
-An abstract index definition for that index, always in hashref format.
+An abstract index definition for that index, always in hashref format.
The hashref will always contain the C<TYPE> element, but it will
be an empty string if it's just a normal index.
=head2 Transaction Methods
-These methods deal with the starting and stopping of transactions
+These methods deal with the starting and stopping of transactions
in the database.
=over
=item C<bz_rollback_transaction>
-Ends a transaction, rolling back all changes. Returns nothing and takes
+Ends a transaction, rolling back all changes. Returns nothing and takes
no parameters.
=back
use Text::ParseWords;
# This is how many comments of MAX_COMMENT_LENGTH we expect on a single bug.
-# In reality, you could have a LOT more comments than this, because
+# In reality, you could have a LOT more comments than this, because
# MAX_COMMENT_LENGTH is big.
use constant MAX_COMMENTS => 50;
# Needs to be explicitly specified for command-line processes.
mysql_auto_reconnect => 1,
);
-
- my $self = $class->db_new({ dsn => $dsn, user => $user,
+
+ my $self = $class->db_new({ dsn => $dsn, user => $user,
pass => $pass, attrs => \%attrs });
# This makes sure that if the tables are encoded as UTF-8, we
$self->{private_bz_dsn} = $dsn;
bless ($self, $class);
-
+
# Bug 321645 - disable MySQL strict mode, if set
my ($var, $sql_mode) = $self->selectrow_array(
"SHOW VARIABLES LIKE 'sql\\_mode'");
}
}
- # Allow large GROUP_CONCATs (largely for inserting comments
+ # Allow large GROUP_CONCATs (largely for inserting comments
# into bugs_fulltext).
$self->do('SET SESSION group_concat_max_len = 128000000');
sub sql_string_concat {
my ($self, @params) = @_;
-
+
return 'CONCAT(' . join(', ', @params) . ')';
}
sub sql_istring {
my ($self, $string) = @_;
-
+
return $string;
}
my ($self, $date, $format) = @_;
$format = "%Y.%m.%d %H:%i:%s" if !$format;
-
+
return "DATE_FORMAT($date, " . $self->quote($format) . ")";
}
sub sql_date_math {
my ($self, $date, $operator, $interval, $units) = @_;
-
+
return "$date $operator INTERVAL $interval $units";
}
my ($sd_index_deleted, $longdescs_index_deleted);
my @tables = $self->bz_table_list_real();
- # We want to convert tables to InnoDB, but it's possible that they have
+ # We want to convert tables to InnoDB, but it's possible that they have
# fulltext indexes on them, and conversion will fail unless we remove
# the indexes.
if (grep($_ eq 'bugs', @tables)
# Upgrade tables from MyISAM to InnoDB
my $db_name = Bugzilla->localconfig->{db_name};
my $myisam_tables = $self->selectcol_arrayref(
- 'SELECT TABLE_NAME FROM information_schema.TABLES
+ 'SELECT TABLE_NAME FROM information_schema.TABLES
WHERE TABLE_SCHEMA = ? AND ENGINE = ?',
undef, $db_name, 'MyISAM');
foreach my $should_be_myisam (Bugzilla::DB::Schema::Mysql::MYISAM_TABLES) {
print "done.\n";
}
}
-
- # Versions of Bugzilla before the existence of Bugzilla::DB::Schema did
+
+ # Versions of Bugzilla before the existence of Bugzilla::DB::Schema did
# not provide explicit names for the table indexes. This means
# that our upgrades will not be reliable, because we look for the name
# of the index, not what fields it is on, when doing upgrades.
- # (using the name is much better for cross-database compatibility
+ # (using the name is much better for cross-database compatibility
# and general reliability). It's also very important that our
# Schema object be consistent with what is on the disk.
#
# to handle basic MySQL stuff.
my $rename_time = int($bug_count / 3000) + 3;
# And 45 minutes for every 15,000 attachments, per some experiments.
- my ($attachment_count) =
+ my ($attachment_count) =
$self->selectrow_array("SELECT COUNT(*) FROM attachments");
$rename_time += int(($attachment_count * 45) / 15000);
# If we're going to take longer than 5 minutes, we let the user know
}
# The email_setting table also had the same problem.
- if( grep($_ eq 'email_setting', @tables)
- && $self->bz_index_info_real('email_setting',
- 'email_settings_user_id_idx') )
+ if( grep($_ eq 'email_setting', @tables)
+ && $self->bz_index_info_real('email_setting',
+ 'email_settings_user_id_idx') )
{
- $self->bz_drop_index_raw('email_setting',
+ $self->bz_drop_index_raw('email_setting',
'email_settings_user_id_idx');
}
# Go through all the tables.
foreach my $table (@tables) {
- # Will contain the names of old indexes as keys, and the
+ # Will contain the names of old indexes as keys, and the
# definition of the new indexes as a value. The values
- # include an extra hash key, NAME, with the new name of
+ # include an extra hash key, NAME, with the new name of
# the index.
my %rename_indexes;
# And go through all the columns on each table.
if ($table eq 'series_categories') {
# The series_categories index had a nonstandard name.
push(@columns, 'series_cats_unique_idx');
- }
- elsif ($table eq 'email_setting') {
+ }
+ elsif ($table eq 'email_setting') {
# The email_setting table had a similar problem.
push(@columns, 'email_settings_unique_idx');
}
push(@columns, @{$bad_names->{$table}})
if (exists $bad_names->{$table});
foreach my $column (@columns) {
- # If we have an index named after this column, it's an
+ # If we have an index named after this column, it's an
# old-style-name index.
if (my $index = $self->bz_index_info_real($table, $column)) {
# Fix the name to fit in with the new naming scheme.
$index->{NAME} = $table . "_" .
$index->{FIELDS}->[0] . "_idx";
- print "Renaming index $column to "
+ print "Renaming index $column to "
. $index->{NAME} . "...\n";
$rename_indexes{$column} = $index;
} # if
# This kind of situation happens when people create the database
# themselves, and if we don't do this they will get the big
# scary WARNING statement about conversion to UTF8.
- if ( !$self->bz_db_is_utf8 && !@tables
+ if ( !$self->bz_db_is_utf8 && !@tables
&& (Bugzilla->params->{'utf8'} || !scalar keys %{Bugzilla->params}) )
{
$self->_alter_db_charset_to_utf8();
$self->_bz_store_real_schema;
}
if ($longdescs_index_deleted) {
- $self->_bz_real_schema->delete_index('longdescs',
+ $self->_bz_real_schema->delete_index('longdescs',
'longdescs_thetext_idx');
$self->_bz_store_real_schema;
}
# 2005-09-24 - bugreport@peshkin.net, bug 307602
# Make sure that default 4G table limit is overridden
my $attach_data_create = $self->selectrow_array(
- 'SELECT CREATE_OPTIONS FROM information_schema.TABLES
+ 'SELECT CREATE_OPTIONS FROM information_schema.TABLES
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?',
undef, $db_name, 'attach_data');
if ($attach_data_create !~ /MAX_ROWS/i) {
#
# TABLE_COLLATION IS NOT NULL prevents us from trying to convert views.
my $non_utf8_tables = $self->selectrow_array(
- "SELECT 1 FROM information_schema.TABLES
- WHERE TABLE_SCHEMA = ? AND TABLE_COLLATION IS NOT NULL
- AND TABLE_COLLATION NOT LIKE 'utf8%'
+ "SELECT 1 FROM information_schema.TABLES
+ WHERE TABLE_SCHEMA = ? AND TABLE_COLLATION IS NOT NULL
+ AND TABLE_COLLATION NOT LIKE 'utf8%'
LIMIT 1", undef, $db_name);
-
+
if (Bugzilla->params->{'utf8'} && $non_utf8_tables) {
print "\n", install_string('mysql_utf8_conversion');
if (!Bugzilla->installation_answers->{NO_PAUSE}) {
- if (Bugzilla->installation_mode ==
- INSTALLATION_MODE_NON_INTERACTIVE)
+ if (Bugzilla->installation_mode ==
+ INSTALLATION_MODE_NON_INTERACTIVE)
{
die install_string('continue_without_answers'), "\n";
}
# If this particular column isn't stored in utf-8
if ($column->{Collation}
- && $column->{Collation} ne 'NULL'
- && $column->{Collation} !~ /utf8/)
+ && $column->{Collation} ne 'NULL'
+ && $column->{Collation} !~ /utf8/)
{
my $name = $column->{Field};
my $raw_default = $raw_info->{COLUMN_DEF};
if (defined $raw_default) {
if ($raw_default eq '') {
- # Only (var)char columns can have empty strings as
+ # Only (var)char columns can have empty strings as
# defaults, so if we got an empty string for some
# other default type, then it's bogus.
next unless $abs_def->{TYPE} =~ /char/i;
print "Fixing defaults...\n";
foreach my $table (reverse sort keys %fix_columns) {
- my @alters = map("ALTER COLUMN $_ DROP DEFAULT",
+ my @alters = map("ALTER COLUMN $_ DROP DEFAULT",
@{ $fix_columns{$table} });
my $sql = "ALTER TABLE $table " . join(',', @alters);
$self->do($sql);
sub _alter_db_charset_to_utf8 {
my $self = shift;
my $db_name = Bugzilla->localconfig->{db_name};
- $self->do("ALTER DATABASE $db_name CHARACTER SET utf8");
+ $self->do("ALTER DATABASE $db_name CHARACTER SET utf8");
}
sub bz_db_is_utf8 {
=head1 MYSQL-SPECIFIC DATABASE-READING METHODS
These methods read information about the database from the disk,
-instead of from a Schema object. They are only reliable for MySQL
+instead of from a Schema object. They are only reliable for MySQL
(see bug 285111 for the reasons why not all DBs use/have functions
-like this), but that's OK because we only need them for
+like this), but that's OK because we only need them for
backwards-compatibility anyway, for versions of Bugzilla before 2.20.
=over 4
my ($self, $table, $column) = @_;
# DBD::mysql does not support selecting a specific column,
- # so we have to get all the columns on the table and find
+ # so we have to get all the columns on the table and find
# the one we want.
my $info_sth = $self->column_info(undef, undef, $table, '%');
=item C<bz_index_list_real($table)>
- Description: Returns a list of index names on a table in
+ Description: Returns a list of index names on a table in
the database, as it actually exists on disk.
Params: $table - The name of the table you want info about.
Returns: An array of index names.
=head1 MYSQL-SPECIFIC "SCHEMA BUILDER"
-MySQL needs to be able to read in a legacy database (from before
+MySQL needs to be able to read in a legacy database (from before
Schema existed) and create a Schema object out of it. That's what
this code does.
my @tables = $self->bz_table_list_real();
if (@tables) {
- print "Building Schema object from database...\n";
+ print "Building Schema object from database...\n";
}
foreach my $table (@tables) {
$schema->add_table($table);
foreach my $index (@indexes) {
unless ($index eq 'PRIMARY') {
my $index_info = $self->bz_index_info_real($table, $index);
- ($index_info = $index_info->{FIELDS})
+ ($index_info = $index_info->{FIELDS})
if (!$index_info->{TYPE});
$schema->set_index($table, $index, $index_info);
}
sub new {
my ($class, $params) = @_;
- my ($user, $pass, $host, $dbname, $port) =
+ my ($user, $pass, $host, $dbname, $port) =
@$params{qw(db_user db_pass db_host db_name db_port)};
# You can never connect to Oracle without a DB name,
# construct the DSN from the parameters we got
my $dsn = "dbi:Oracle:host=$host;sid=$dbname";
$dsn .= ";port=$port" if $port;
- my $attrs = { FetchHashKeyName => 'NAME_lc',
+ my $attrs = { FetchHashKeyName => 'NAME_lc',
LongReadLen => max(Bugzilla->params->{'maxattachmentsize'} || 0,
MIN_LONG_READ_LEN) * 1024,
};
- my $self = $class->db_new({ dsn => $dsn, user => $user,
+ my $self = $class->db_new({ dsn => $dsn, user => $user,
pass => $pass, attrs => $attrs });
# Needed by TheSchwartz
$self->{private_bz_dsn} = $dsn;
# Set the session's default date format to match MySQL
$self->do("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
$self->do("ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS'");
- $self->do("ALTER SESSION SET NLS_LENGTH_SEMANTICS='CHAR'")
+ $self->do("ALTER SESSION SET NLS_LENGTH_SEMANTICS='CHAR'")
if Bugzilla->params->{'utf8'};
# To allow case insensitive query.
$self->do("ALTER SESSION SET NLS_COMP='ANSI'");
{ value => $pattern, dberror => $self->errstr });
}
-sub bz_explain {
- my ($self, $sql) = @_;
- my $sth = $self->prepare("EXPLAIN PLAN FOR $sql");
+sub bz_explain {
+ my ($self, $sql) = @_;
+ my $sth = $self->prepare("EXPLAIN PLAN FOR $sql");
$sth->execute();
my $explain = $self->selectcol_arrayref(
"SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY)");
- return join("\n", @$explain);
-}
+ return join("\n", @$explain);
+}
sub sql_group_concat {
my ($self, $text, $separator) = @_;
$self->bz_check_regexp($real_pattern) if !$nocheck;
- return "NOT REGEXP_LIKE($expr, $pattern)"
+ return "NOT REGEXP_LIKE($expr, $pattern)"
}
sub sql_limit {
sub sql_date_format {
my ($self, $date, $format) = @_;
-
+
$format = "%Y.%m.%d %H:%i:%s" if !$format;
$format =~ s/\%Y/YYYY/g;
my $length = $#in_list + 1;
my $splice = $length > 1000 ? 1000 : $length;
my @sub_in_list = splice(@in_list, 0, $splice);
- push(@in_str,
+ push(@in_str,
$self->SUPER::sql_in($column_name, \@sub_in_list, $negate));
}
return "( " . join(" OR ", @in_str) . " )";
}
}
-# Dropping all FKs for a specified table.
+# Dropping all FKs for a specified table.
sub _bz_drop_fks {
my ($self, $table) = @_;
my @columns = $self->bz_table_columns($table);
sub adjust_statement {
my ($sql) = @_;
-
+
if ($sql =~ /^CREATE OR REPLACE.*/i){
return $sql;
- }
+ }
# We can't just assume any occurrence of "''" in $sql is an empty
# string, since "''" can occur inside a string literal as a way of
# number of parts
my @result;
my $part = shift @parts;
-
+
# Oracle requires a FROM clause in all SELECT statements, so append
# "FROM dual" to queries without one (e.g., "SELECT NOW()")
my $is_select = ($part =~ m/^\s*SELECT\b/io);
# Oracle use SUBSTR instead of SUBSTRING
$part =~ s/\bSUBSTRING\b/SUBSTR/io;
-
+
# Oracle need no 'AS'
$part =~ s/\bAS\b//ig;
-
+
# Oracle doesn't have LIMIT, so if we find the LIMIT comment, wrap the
# query with "SELECT * FROM (...) WHERE rownum < $limit"
my ($limit,$offset) = ($part =~ m{/\* LIMIT (\d*) (\d*) \*/}o);
-
+
push @result, $part;
while( @parts ) {
my $string = shift @parts;
my $nonstring = shift @parts;
-
+
# if the non-string part is zero-length and there are more parts left,
- # then this is an escaped quote inside a string literal
+ # then this is an escaped quote inside a string literal
while( !(length $nonstring) && @parts ) {
# we know it's safe to remove two parts at a time, since we
# entered the loop with an even number of parts
}
# Look for a FROM if this is a SELECT and we haven't found one yet
- $has_from = ($nonstring =~ m/\bFROM\b/io)
+ $has_from = ($nonstring =~ m/\bFROM\b/io)
if ($is_select and !$has_from);
# Oracle recognizes CURRENT_DATE, but not CURRENT_DATE()
# Oracle use SUBSTR instead of SUBSTRING
$nonstring =~ s/\bSUBSTRING\b/SUBSTR/io;
-
+
# Oracle need no 'AS'
$nonstring =~ s/\bAS\b//ig;
-
- # Take the first 4000 chars for comparison
+
+ # Take the first 4000 chars for comparison
$nonstring =~ s/\(\s*(longdescs_\d+\.thetext|attachdata_\d+\.thedata)/
\(DBMS_LOB.SUBSTR\($1, 4000, 1\)/ig;
$new_sql .= " FROM DUAL" if ($is_select and !$has_from);
# Wrap the query with a "WHERE rownum <= ..." if we found LIMIT
-
+
if (defined($limit)) {
if ($new_sql !~ /\bWHERE\b/) {
$new_sql = $new_sql." WHERE 1=1";
my ($before_from, $after_from) = split(/\bFROM\b/i, $new_sql, 2);
$before_where = "$before_from FROM ($before_from,"
. " ROW_NUMBER() OVER (ORDER BY 1) R "
- . " FROM $after_from ) ";
+ . " FROM $after_from ) ";
$after_where = " R BETWEEN $offset+1 AND $limit+$offset";
} else {
$after_where = " rownum <=$limit AND ".$after_where;
unshift @_, $new_stmt;
my $ref = $self->SUPER::selectall_arrayref(@_);
return undef if !defined $ref;
-
+
foreach my $row (@$ref) {
if (ref($row) eq 'ARRAY') {
_fix_arrayref($row);
unshift @_, $new_stmt;
my $rows = $self->SUPER::selectall_hashref(@_);
return undef if !defined $rows;
- foreach my $row (values %$rows) {
+ foreach my $row (values %$rows) {
_fix_hashref($row);
}
return $rows;
my ($self, $table) = @_;
$table = uc($table);
my $cols = $self->selectcol_arrayref(
- "SELECT LOWER(COLUMN_NAME) FROM USER_TAB_COLUMNS WHERE
+ "SELECT LOWER(COLUMN_NAME) FROM USER_TAB_COLUMNS WHERE
TABLE_NAME = ? ORDER BY COLUMN_NAME", undef, $table);
return @$cols;
}
sub bz_table_list_real {
my ($self) = @_;
my $tables = $self->selectcol_arrayref(
- "SELECT LOWER(TABLE_NAME) FROM USER_TABLES WHERE
+ "SELECT LOWER(TABLE_NAME) FROM USER_TABLES WHERE
TABLE_NAME NOT LIKE ? ORDER BY TABLE_NAME", undef, 'DR$%');
return @$tables;
}
sub bz_setup_database {
my $self = shift;
-
+
# Create a function that returns SYSDATE to emulate MySQL's "NOW()".
- # Function NOW() is used widely in Bugzilla SQLs, but Oracle does not
- # have that function, So we have to create one ourself.
+ # Function NOW() is used widely in Bugzilla SQLs, but Oracle does not
+ # have that function, So we have to create one ourself.
$self->do("CREATE OR REPLACE FUNCTION NOW "
. " RETURN DATE IS BEGIN RETURN SYSDATE; END;");
- $self->do("CREATE OR REPLACE FUNCTION CHAR_LENGTH(COLUMN_NAME VARCHAR2)"
+ $self->do("CREATE OR REPLACE FUNCTION CHAR_LENGTH(COLUMN_NAME VARCHAR2)"
. " RETURN NUMBER IS BEGIN RETURN LENGTH(COLUMN_NAME); END;");
-
+
# Create types for group_concat
my $type_exists = $self->selectrow_array("SELECT 1 FROM user_types
WHERE type_name = 'T_GROUP_CONCAT'");
END;
END;");
- $self->do("CREATE OR REPLACE TYPE T_GROUP_CONCAT AS OBJECT
+ $self->do("CREATE OR REPLACE TYPE T_GROUP_CONCAT AS OBJECT
( CLOB_CONTENT CLOB,
DELIMITER VARCHAR2(256),
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(
RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATEITERATE(
SELF IN OUT NOCOPY T_GROUP_CONCAT,
- VALUE IN T_CLOB_DELIM)
+ VALUE IN T_CLOB_DELIM)
RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
SELF IN T_GROUP_CONCAT,
RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATEMERGE(
SELF IN OUT NOCOPY T_GROUP_CONCAT,
- CTX2 IN T_GROUP_CONCAT)
+ CTX2 IN T_GROUP_CONCAT)
RETURN NUMBER);");
$self->do("CREATE OR REPLACE TYPE BODY T_GROUP_CONCAT IS
END;
MEMBER FUNCTION ODCIAGGREGATEITERATE(
SELF IN OUT NOCOPY T_GROUP_CONCAT,
- VALUE IN T_CLOB_DELIM)
+ VALUE IN T_CLOB_DELIM)
RETURN NUMBER IS
BEGIN
SELF.DELIMITER := VALUE.P_DELIMITER;
- DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
+ DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
LENGTH(SELF.DELIMITER),
SELF.DELIMITER);
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, VALUE.P_CONTENT);
-
+
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
SELF IN T_GROUP_CONCAT,
RETURNVALUE OUT NOCOPY CLOB,
- FLAGS IN NUMBER)
+ FLAGS IN NUMBER)
RETURN NUMBER IS
BEGIN
- RETURNVALUE := RTRIM(LTRIM(SELF.CLOB_CONTENT,
- SELF.DELIMITER),
+ RETURNVALUE := RTRIM(LTRIM(SELF.CLOB_CONTENT,
+ SELF.DELIMITER),
SELF.DELIMITER);
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATEMERGE(
SELF IN OUT NOCOPY T_GROUP_CONCAT,
- CTX2 IN T_GROUP_CONCAT)
+ CTX2 IN T_GROUP_CONCAT)
RETURN NUMBER IS
BEGIN
- DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
- LENGTH(SELF.DELIMITER),
+ DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
+ LENGTH(SELF.DELIMITER),
SELF.DELIMITER);
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, CTX2.CLOB_CONTENT);
RETURN ODCICONST.SUCCESS;
END;");
# Create user-defined aggregate function group_concat
- $self->do("CREATE OR REPLACE FUNCTION GROUP_CONCAT(P_INPUT T_CLOB_DELIM)
- RETURN CLOB
+ $self->do("CREATE OR REPLACE FUNCTION GROUP_CONCAT(P_INPUT T_CLOB_DELIM)
+ RETURN CLOB
DETERMINISTIC PARALLEL_ENABLE AGGREGATE USING T_GROUP_CONCAT;");
# Create a WORLD_LEXER named BZ_LEX for multilingual fulltext search
if(@$exist_trigger) {
$self->do("DROP TRIGGER $trigger_name");
}
-
+
my $tr_str = "CREATE OR REPLACE TRIGGER $trigger_name"
. " AFTER UPDATE OF $to_column ON $to_table "
. " REFERENCING "
package Bugzilla::DB::Oracle::st;
use base qw(DBI::st);
-
+
sub fetchrow_arrayref {
my $self = shift;
my $ref = $self->SUPER::fetchrow_arrayref(@_);
sub new {
my ($class, $params) = @_;
- my ($user, $pass, $host, $dbname, $port) =
+ my ($user, $pass, $host, $dbname, $port) =
@$params{qw(db_user db_pass db_host db_name db_port)};
# The default database name for PostgreSQL. We have
my $attrs = { pg_enable_utf8 => Bugzilla->params->{'utf8'} };
- my $self = $class->db_new({ dsn => $dsn, user => $user,
+ my $self = $class->db_new({ dsn => $dsn, user => $user,
pass => $pass, attrs => $attrs });
# all class local variables stored in DBI derived class needs to have
$self->bz_check_regexp($real_pattern) if !$nocheck;
- return "${expr}::text !~* $pattern"
+ return "${expr}::text !~* $pattern"
}
sub sql_limit {
sub sql_date_format {
my ($self, $date, $format) = @_;
-
+
$format = "%Y.%m.%d %H:%i:%s" if !$format;
$format =~ s/\%Y/YYYY/g;
sub sql_date_math {
my ($self, $date, $operator, $interval, $units) = @_;
-
+
return "$date $operator $interval * INTERVAL '1 $units'";
}
sub sql_string_concat {
my ($self, @params) = @_;
-
+
# Postgres 7.3 does not support concatenating of different types, so we
# need to cast both parameters to text. Version 7.4 seems to handle this
# properly, so when we stop support 7.3, this can be removed.
SFUNC = array_append,
BASETYPE = anyelement,
STYPE = anyarray,
- INITCOND = '{}'
+ INITCOND = '{}'
)");
}
$self->bz_drop_index('longdescs', 'longdescs_thetext_idx');
# Same for all the comments fields in the fulltext table.
$self->bz_drop_index('bugs_fulltext', 'bugs_fulltext_comments_idx');
- $self->bz_drop_index('bugs_fulltext',
+ $self->bz_drop_index('bugs_fulltext',
'bugs_fulltext_comments_noprivate_idx');
# PostgreSQL also wants an index for calling LOWER on
# login_name, which we do with sql_istrcmp all over the place.
- $self->bz_add_index('profiles', 'profiles_login_name_lower_idx',
+ $self->bz_add_index('profiles', 'profiles_login_name_lower_idx',
{FIELDS => ['LOWER(login_name)'], TYPE => 'UNIQUE'});
# Now that Bugzilla::Object uses sql_istrcmp, other tables
my $dbh = Bugzilla->dbh;
my $duplicates = $dbh->selectcol_arrayref(
- "SELECT DISTINCT LOWER($field) FROM $table
+ "SELECT DISTINCT LOWER($field) FROM $table
GROUP BY LOWER($field) HAVING COUNT(LOWER($field)) > 1");
foreach my $name (@$duplicates) {
# Custom Schema Information Functions
#####################################################################
-# Pg includes the PostgreSQL system tables in table_list_real, so
+# Pg includes the PostgreSQL system tables in table_list_real, so
# we need to remove those.
sub bz_table_list_real {
my $self = shift;
is incremented every time the the fundamental structure of Schema
internals changes.
-This is NOT changed every time a table or a column is added. This
-number is incremented only if the internal structures of this
-Schema would be incompatible with the internal structures of a
+This is NOT changed every time a table or a column is added. This
+number is incremented only if the internal structures of this
+Schema would be incompatible with the internal structures of a
previous Schema version.
In general, unless you are messing around with serialization
to change it so that the schema instead uses NULL => 0.
But we have a bunch of Bugzilla installations around the world with a
-serialized schema that has NOTNULL in it! When we deserialize that
+serialized schema that has NOTNULL in it! When we deserialize that
structure, it just WILL NOT WORK properly inside of our new Schema object.
-So, immediately after deserializing, we need to go through the hash
+So, immediately after deserializing, we need to go through the hash
and change all NOTNULLs to NULLs and so on.
We know that we need to do that on deserializing because we know that
The value for each key is a hash reference containing the keys
C<FIELDS> and C<INDEXES> which in turn point to array references
-containing information on the table's fields and indexes.
+containing information on the table's fields and indexes.
A field hash reference should must contain the key C<TYPE>. Optional field
-keys include C<PRIMARYKEY>, C<NOTNULL>, and C<DEFAULT>.
+keys include C<PRIMARYKEY>, C<NOTNULL>, and C<DEFAULT>.
-The C<INDEXES> array reference contains index names and information
+The C<INDEXES> array reference contains index names and information
regarding the index. If the index name points to an array reference,
then the index is a regular index and the array contains the indexed
columns. If the index name points to a hash reference, then the hash
=item C<DELETE>
What to do if the row in the parent table is deleted. Choices are
-C<RESTRICT>, C<CASCADE>, or C<SET NULL>.
+C<RESTRICT>, C<CASCADE>, or C<SET NULL>.
-C<RESTRICT> means the deletion of the row in the parent table will
-be forbidden by the database if there is a row in I<this> table that
+C<RESTRICT> means the deletion of the row in the parent table will
+be forbidden by the database if there is a row in I<this> table that
still refers to it. This is the default, if you don't specify
C<DELETE>.
What to do if the value in the parent table is updated. You can set this
to C<CASCADE> or C<RESTRICT>, which mean the same thing as they do for
-L</DELETE>. This variable defaults to C<CASCADE>, which means "also
+L</DELETE>. This variable defaults to C<CASCADE>, which means "also
update this column in this table."
=back
assigned_to => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid'}},
- bug_file_loc => {TYPE => 'MEDIUMTEXT',
+ bug_file_loc => {TYPE => 'MEDIUMTEXT',
NOTNULL => 1, DEFAULT => "''"},
bug_severity => {TYPE => 'varchar(64)', NOTNULL => 1},
bug_status => {TYPE => 'varchar(64)', NOTNULL => 1},
bugs_activity => {
FIELDS => [
- id => {TYPE => 'INTSERIAL', NOTNULL => 1,
- PRIMARYKEY => 1},
+ id => {TYPE => 'INTSERIAL', NOTNULL => 1,
+ PRIMARYKEY => 1},
bug_id => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'bugs',
COLUMN => 'bug_id',
COLUMN => 'id'}},
added => {TYPE => 'varchar(255)'},
removed => {TYPE => 'varchar(255)'},
- comment_id => {TYPE => 'INT4',
+ comment_id => {TYPE => 'INT4',
REFERENCES => { TABLE => 'longdescs',
COLUMN => 'comment_id',
DELETE => 'CASCADE'}},
bugs_activity_bug_when_idx => ['bug_when'],
bugs_activity_fieldid_idx => ['fieldid'],
bugs_activity_added_idx => ['added'],
- bugs_activity_removed_idx => ['removed'],
+ bugs_activity_removed_idx => ['removed'],
],
},
class => {TYPE => 'varchar(255)', NOTNULL => 1, DEFAULT => "''"},
],
INDEXES => [
- bug_see_also_bug_id_idx => {FIELDS => [qw(bug_id value)],
+ bug_see_also_bug_id_idx => {FIELDS => [qw(bug_id value)],
TYPE => 'UNIQUE'},
],
},
DEFAULT => 'FALSE'},
buglist => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
- visibility_field_id => {TYPE => 'INT3',
+ visibility_field_id => {TYPE => 'INT3',
REFERENCES => {TABLE => 'fielddefs',
COLUMN => 'id'}},
value_field_id => {TYPE => 'INT3',
field_visibility => {
FIELDS => [
- field_id => {TYPE => 'INT3',
+ field_id => {TYPE => 'INT3',
REFERENCES => {TABLE => 'fielddefs',
COLUMN => 'id',
DELETE => 'CASCADE'}},
REFERENCES => {TABLE => 'products',
COLUMN => 'id',
DELETE => 'CASCADE'}},
- isactive => {TYPE => 'BOOLEAN', NOTNULL => 1,
+ isactive => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'TRUE'},
],
INDEXES => [
milestones => {
FIELDS => [
- id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
PRIMARYKEY => 1},
product_id => {TYPE => 'INT2', NOTNULL => 1,
REFERENCES => {TABLE => 'products',
value => {TYPE => 'varchar(20)', NOTNULL => 1},
sortkey => {TYPE => 'INT2', NOTNULL => 1,
DEFAULT => 0},
- isactive => {TYPE => 'BOOLEAN', NOTNULL => 1,
+ isactive => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'TRUE'},
],
INDEXES => [
FIELDS => [
# On bug creation, there is no old value.
old_status => {TYPE => 'INT2',
- REFERENCES => {TABLE => 'bug_status',
+ REFERENCES => {TABLE => 'bug_status',
COLUMN => 'id',
DELETE => 'CASCADE'}},
new_status => {TYPE => 'INT2', NOTNULL => 1,
- REFERENCES => {TABLE => 'bug_status',
+ REFERENCES => {TABLE => 'bug_status',
COLUMN => 'id',
DELETE => 'CASCADE'}},
require_comment => {TYPE => 'INT1', NOTNULL => 1, DEFAULT => 0},
profile_search => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
- user_id => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
- COLUMN => 'userid',
+ user_id => {TYPE => 'INT3', NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles',
+ COLUMN => 'userid',
DELETE => 'CASCADE'}},
bug_list => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
list_order => {TYPE => 'MEDIUMTEXT'},
profiles_activity => {
FIELDS => [
- id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
- PRIMARYKEY => 1},
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
+ PRIMARYKEY => 1},
userid => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
+ REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'CASCADE'}},
who => {TYPE => 'INT3', NOTNULL => 1,
ip_addr => {TYPE => 'varchar(40)', NOTNULL => 1},
],
INDEXES => [
- # We do lookups by every item in the table simultaneously, but
+ # We do lookups by every item in the table simultaneously, but
# having an index with all three items would be the same size as
- # the table. So instead we have an index on just the smallest item,
+ # the table. So instead we have an index on just the smallest item,
# to speed lookups.
login_failure_user_id_idx => ['user_id'],
],
id => {TYPE => 'MEDIUMSERIAL', PRIMARYKEY => 1,
NOTNULL => 1},
owner_userid => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
+ REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'CASCADE'}},
subject => {TYPE => 'varchar(128)'},
quipid => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
PRIMARYKEY => 1},
userid => {TYPE => 'INT3',
- REFERENCES => {TABLE => 'profiles',
+ REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'SET NULL'}},
quip => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
setting => {
FIELDS => [
name => {TYPE => 'varchar(32)', NOTNULL => 1,
- PRIMARYKEY => 1},
+ PRIMARYKEY => 1},
default_value => {TYPE => 'varchar(32)', NOTNULL => 1},
is_enabled => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'TRUE'},
setting_value => {
FIELDS => [
name => {TYPE => 'varchar(32)', NOTNULL => 1,
- REFERENCES => {TABLE => 'setting',
+ REFERENCES => {TABLE => 'setting',
COLUMN => 'name',
DELETE => 'CASCADE'}},
value => {TYPE => 'varchar(32)', NOTNULL => 1},
funcname => {TYPE => 'varchar(255)', NOTNULL => 1},
],
INDEXES => [
- ts_funcmap_funcname_idx => {FIELDS => ['funcname'],
+ ts_funcmap_funcname_idx => {FIELDS => ['funcname'],
TYPE => 'UNIQUE'},
],
},
FIELDS => [
# In a standard TheSchwartz schema, this is a BIGINT, but we
# don't have those and I didn't want to add them just for this.
- jobid => {TYPE => 'INTSERIAL', PRIMARYKEY => 1,
+ jobid => {TYPE => 'INTSERIAL', PRIMARYKEY => 1,
NOTNULL => 1},
funcid => {TYPE => 'INT4', NOTNULL => 1},
# In standard TheSchwartz, this is a MEDIUMBLOB.
value => {TYPE => 'LONGBLOB'},
],
INDEXES => [
- ts_note_jobid_idx => {FIELDS => [qw(jobid notekey)],
+ ts_note_jobid_idx => {FIELDS => [qw(jobid notekey)],
TYPE => 'UNIQUE'},
],
},
define the database-specific implementation of the all
abstract data types), and then call the C<_adjust_schema>
method.
- Parameters: $abstract_schema (optional) - A reference to a hash. If
+ Parameters: $abstract_schema (optional) - A reference to a hash. If
provided, this hash will be used as the internal
representation of the abstract schema instead of our
- default abstract schema. This is intended for internal
+ default abstract schema. This is intended for internal
use only by deserialize_abstract.
Returns: the instance of the Schema class
# tables. If we don't lock/unlock keys, lock_value complains.
lock_keys(%$abstract_schema);
foreach my $table (keys %{ABSTRACT_SCHEMA()}) {
- lock_value(%$abstract_schema, $table)
+ lock_value(%$abstract_schema, $table)
if exists $abstract_schema->{$table};
}
unlock_keys(%$abstract_schema);
- Bugzilla::Hook::process('db_schema_abstract_schema',
+ Bugzilla::Hook::process('db_schema_abstract_schema',
{ schema => $abstract_schema });
unlock_hash(%$abstract_schema);
}
=item B<Description>
Public method to convert abstract (database-generic) field specifiers to
-database-specific data types suitable for use in a C<CREATE TABLE> or
+database-specific data types suitable for use in a C<CREATE TABLE> or
C<ALTER TABLE> SQL statment. If no database-specific field type has been
defined for the given field type, then it will just return the same field type.
=over
=item C<$def> - A reference to a hash of a field containing the following keys:
-C<TYPE> (required), C<NOTNULL> (optional), C<DEFAULT> (optional),
+C<TYPE> (required), C<NOTNULL> (optional), C<DEFAULT> (optional),
C<PRIMARYKEY> (optional), C<REFERENCES> (optional)
=back
=item B<Returns>
-A DDL string suitable for describing a field in a C<CREATE TABLE> or
+A DDL string suitable for describing a field in a C<CREATE TABLE> or
C<ALTER TABLE> SQL statement
=back
my $self = shift;
my $finfo = (@_ == 1 && ref($_[0]) eq 'HASH') ? $_[0] : { @_ };
my $type = $finfo->{TYPE};
- confess "A valid TYPE was not specified for this column (got "
+ confess "A valid TYPE was not specified for this column (got "
. Dumper($finfo) . ")" unless ($type);
my $default = $finfo->{DEFAULT};
=item B<Returns>
-SQL for to define the foreign key, or an empty string if C<$references>
+SQL for to define the foreign key, or an empty string if C<$references>
is undefined.
=back
# other reasons).
sub _get_fk_name {
my ($self, $table, $column, $references) = @_;
- my $to_table = $references->{TABLE};
+ my $to_table = $references->{TABLE};
my $to_column = $references->{COLUMN};
my $name = "fk_${table}_${column}_${to_table}_${to_column}";
return @ddl;
}
-sub get_drop_fk_sql {
+sub get_drop_fk_sql {
my ($self, $table, $column, $references) = @_;
my $fk_name = $self->_get_fk_name($table, $column, $references);
=cut
my $self = shift;
- return sort keys %{$self->{schema}};
+ return sort keys %{$self->{schema}};
}
sub get_table_columns {
while (@indexes) {
my $index_name = shift(@indexes);
my $index_info = shift(@indexes);
- my $index_sql = $self->get_add_index_ddl($table, $index_name,
+ my $index_sql = $self->get_add_index_ddl($table, $index_name,
$index_info);
push(@ddl, $index_sql) if $index_sql;
}
push(@fk_lines, $fk_ddl);
}
}
-
+
my $sql = "CREATE TABLE $table (\n" . join(",\n", @col_lines, @fk_lines)
. "\n)";
return $sql
-}
+}
sub _get_create_index_ddl {
$column - The name of the column being added.
\%definition - The new definition for the column,
in standard C<ABSTRACT_SCHEMA> format.
- $init_value - (optional) An initial value to set
+ $init_value - (optional) An initial value to set
the column to. Should already be SQL-quoted
if necessary.
Returns: An array of SQL statements.
Description: Gets SQL for creating an index.
NOTE: Subclasses should not override this function. Instead,
- if they need to specify a custom CREATE INDEX statement,
+ if they need to specify a custom CREATE INDEX statement,
they should override C<_get_create_index_ddl>
Params: $table - The name of the table the index will be on.
$name - The name of the new index.
- $definition - An index definition. Either a hashref
- with FIELDS and TYPE or an arrayref
+ $definition - An index definition. Either a hashref
+ with FIELDS and TYPE or an arrayref
containing a list of columns.
- Returns: An array of SQL statements that will create the
+ Returns: An array of SQL statements that will create the
requested index.
=cut
$index_fields = $definition;
$index_type = '';
}
-
- return $self->_get_create_index_ddl($table, $name, $index_fields,
+
+ return $self->_get_create_index_ddl($table, $name, $index_fields,
$index_type);
}
# If the types have changed, we have to deal with that.
if (uc(trim($old_def->{TYPE})) ne uc(trim($new_def->{TYPE}))) {
- push(@statements, $self->_get_alter_type_sql($table, $column,
+ push(@statements, $self->_get_alter_type_sql($table, $column,
$new_def, $old_def));
}
. " DROP DEFAULT");
}
# If we went from no default to a default, or we changed the default.
- elsif ( (defined $default && !defined $default_old) ||
- ($default ne $default_old) )
+ elsif ( (defined $default && !defined $default_old) ||
+ ($default ne $default_old) )
{
push(@statements, "ALTER TABLE $table ALTER COLUMN $column "
. " SET DEFAULT $default");
sub _set_nulls_sql {
my ($self, $table, $column, $new_def, $set_nulls_to) = @_;
my $default = $new_def->{DEFAULT};
- # If we have a set_nulls_to, that overrides the DEFAULT
- # (although nobody would usually specify both a default and
+ # If we have a set_nulls_to, that overrides the DEFAULT
+ # (although nobody would usually specify both a default and
# a set_nulls_to.)
$default = $set_nulls_to if defined $set_nulls_to;
if (defined $default) {
my $abstract_fields = $self->{abstract_schema}{$table}{FIELDS};
my $name_position = firstidx { $_ eq $column } @$abstract_fields;
- die "Attempted to delete nonexistent column ${table}.${column}"
+ die "Attempted to delete nonexistent column ${table}.${column}"
if $name_position == -1;
# Delete the key/value pair from the array.
splice(@$abstract_fields, $name_position, 2);
if you want to do that.
Params: $table - The name of the table that the column is on.
$column - The name of the column.
- \%new_def - The new definition for the column, in
+ \%new_def - The new definition for the column, in
C<ABSTRACT_SCHEMA> format.
Returns: nothing
if you want to do that.
Params: $table - The table the index is on.
$name - The name of the index.
- $definition - A hashref or an arrayref. An index
+ $definition - A hashref or an arrayref. An index
definition in C<ABSTRACT_SCHEMA> format.
Returns: nothing
my $indexes = $self->{abstract_schema}{$table}{INDEXES};
my $name_position = firstidx { $_ eq $name } @$indexes;
- die "Attempted to delete nonexistent index $name on the $table table"
+ die "Attempted to delete nonexistent index $name on the $table table"
if $name_position == -1;
# Delete the key/value pair from the array.
splice(@$indexes, $name_position, 2);
Description: Tells you if two columns have entirely identical definitions.
The TYPE field's value will be compared case-insensitive.
However, all other fields will be case-sensitive.
- Params: $col_one, $col_two - The columns to compare. Hash
+ Params: $col_one, $col_two - The columns to compare. Hash
references, in C<ABSTRACT_SCHEMA> format.
Returns: C<1> if the columns are identical, C<0> if they are not.
Do not attempt to manipulate this data directly,
as the format may change at any time in the future.
The only thing you should do with the returned value
- is either store it somewhere (coupled with appropriate
+ is either store it somewhere (coupled with appropriate
SCHEMA_VERSION) or deserialize it.
=cut
sub serialize_abstract {
my ($self) = @_;
-
+
# Make it ok to eval
local $Data::Dumper::Purity = 1;
-
+
# Avoid cross-refs
local $Data::Dumper::Deepcopy = 1;
-
+
# Always sort keys to allow textual compare
local $Data::Dumper::Sortkeys = 1;
-
+
return Dumper($self->{abstract_schema});
}
$version - A number. The "version"
of the Schema that did the serialization.
See the docs for C<SCHEMA_VERSION> for more details.
- Returns: A Schema object. It will have the methods of (and work
- in the same fashion as) the current version of Schema.
+ Returns: A Schema object. It will have the methods of (and work
+ in the same fashion as) the current version of Schema.
However, it will represent the serialized data instead of
ABSTRACT_SCHEMA.
if ($version < 3) {
my $standard = $class->new()->{abstract_schema};
foreach my $table_name (keys %$thawed_hash) {
- my %standard_fields =
+ my %standard_fields =
@{ $standard->{$table_name}->{FIELDS} || [] };
my $table = $thawed_hash->{$table_name};
my %fields = @{ $table->{FIELDS} || [] };
=item C<INT4>
-Integer values (-2,147,483,648 - 2,147,483,647 or 0 - 4,294,967,295
+Integer values (-2,147,483,648 - 2,147,483,647 or 0 - 4,294,967,295
unsigned)
=item C<SMALLSERIAL>
=item C<DATETIME>
-DATETIME support varies from database to database, however, it's generally
+DATETIME support varies from database to database, however, it's generally
safe to say that DATETIME entries support all date/time combinations greater
than 1900-01-01 00:00:00. Note that the format used is C<YYYY-MM-DD hh:mm:ss>
to be safe, though it's possible that your database may not require
-leading zeros. For greatest compatibility, however, please make sure dates
+leading zeros. For greatest compatibility, however, please make sure dates
are formatted as above for queries to guarantee consistent results.
=back
use base qw(Bugzilla::DB::Schema);
-# This is for column_info_to_column, to know when a tinyint is a
+# This is for column_info_to_column, to know when a tinyint is a
# boolean and when it's really a tinyint. This only has to be accurate
# up to and through 2.19.3, because that's the only time we need
# column_info_to_column.
# that should be interpreted as a BOOLEAN instead of as an INT1 when
# reading in the Schema from the disk. The values are discarded; I just
# used "1" for simplicity.
-#
+#
# THIS CONSTANT IS ONLY USED FOR UPGRADES FROM 2.18 OR EARLIER. DON'T
# UPDATE IT TO MODERN COLUMN NAMES OR DEFINITIONS.
use constant BOOLEAN_MAP => {
longdescs => {isprivate => 1, already_wrapped => 1},
attachments => {ispatch => 1, isobsolete => 1, isprivate => 1},
flags => {is_active => 1},
- flagtypes => {is_active => 1, is_requestable => 1,
+ flagtypes => {is_active => 1, is_requestable => 1,
is_requesteeble => 1, is_multiplicable => 1},
fielddefs => {mailhead => 1, obsolete => 1},
bug_status => {isactive => 1},
$self->{db_specific} = {
BOOLEAN => 'tinyint',
- FALSE => '0',
+ FALSE => '0',
TRUE => '1',
INT1 => 'tinyint',
my $charset = Bugzilla->dbh->bz_db_is_utf8 ? "CHARACTER SET utf8" : '';
my $type = grep($_ eq $table, MYISAM_TABLES) ? 'MYISAM' : 'InnoDB';
- return($self->SUPER::_get_create_table_ddl($table)
+ return($self->SUPER::_get_create_table_ddl($table)
. " ENGINE = $type $charset");
} #eosub--_get_create_table_ddl
my ($self, $name) = @_;
# We only create as utf8 if we have no params (meaning we're doing
# a new installation) or if the utf8 param is on.
- my $create_utf8 = Bugzilla->params->{'utf8'}
+ my $create_utf8 = Bugzilla->params->{'utf8'}
|| !defined Bugzilla->params->{'utf8'};
my $charset = $create_utf8 ? "CHARACTER SET utf8" : '';
return ("CREATE DATABASE $name $charset");
delete $old_defaultless{DEFAULT};
delete $new_defaultless{DEFAULT};
if (!$self->columns_equal($old_def, $new_def)
- && $self->columns_equal(\%new_defaultless, \%old_defaultless))
+ && $self->columns_equal(\%new_defaultless, \%old_defaultless))
{
if (!defined $new_def->{DEFAULT}) {
push(@statements,
}
else {
my $new_ddl = $self->get_type_ddl(\%new_def_copy);
- push(@statements, "ALTER TABLE $table CHANGE COLUMN
+ push(@statements, "ALTER TABLE $table CHANGE COLUMN
$column $column $new_ddl");
}
# MySQL requires, and will create, an index on any column with
# an FK. It will name it after the fk, which we never do.
- # So if there's an index named after the fk, we also have to delete it.
+ # So if there's an index named after the fk, we also have to delete it.
if ($dbh->bz_index_info_real($table, $fk_name)) {
push(@sql, $self->get_drop_index_ddl($table, $fk_name));
}
}
# A special function for MySQL, for renaming a lot of indexes.
-# Index renames is a hash, where the key is a string - the
+# Index renames is a hash, where the key is a string - the
# old names of the index, and the value is a hash - the index
# definition that we're renaming to, with an extra key of "NAME"
# that contains the new index name.
# be dropping it soon, or it's a custom end-user column, in which
# case having a bogus default won't harm anything.
my $schema_column = $self->get_column($table, $col_name);
- unless ( (!$column_info->{COLUMN_DEF}
+ unless ( (!$column_info->{COLUMN_DEF}
|| $column_info->{COLUMN_DEF} eq '0000-00-00 00:00:00'
|| $column_info->{COLUMN_DEF} eq '0.00')
- && $schema_column
+ && $schema_column
&& !exists $schema_column->{DEFAULT}) {
-
+
my $default = $column_info->{COLUMN_DEF};
- # Schema uses '0' for the defaults for decimal fields.
+ # Schema uses '0' for the defaults for decimal fields.
$default = 0 if $default =~ /^0\.0+$/;
# If we're not a number, we're a string and need to be
# quoted.
}
elsif ($type eq 'SMALLINT') {
$type = 'SMALLSERIAL';
- }
+ }
else {
$type = 'INTSERIAL';
}
}
- # For all other db-specific types, check if they exist in
+ # For all other db-specific types, check if they exist in
# REVERSE_MAPPING and use the type found there.
if (exists REVERSE_MAPPING->{$type}) {
$type = REVERSE_MAPPING->{$type};
$self->{db_specific} = {
BOOLEAN => 'integer',
- FALSE => '0',
+ FALSE => '0',
TRUE => '1',
INT1 => 'integer',
while (@fields) {
my $field_name = shift @fields;
my $field_info = shift @fields;
- # Create triggers to deal with empty string.
- if ( $field_info->{TYPE} =~ /varchar|TEXT/i
+ # Create triggers to deal with empty string.
+ if ( $field_info->{TYPE} =~ /varchar|TEXT/i
&& $field_info->{NOTNULL} ) {
push (@ddl, _get_notnull_trigger_ddl($table, $field_name));
}
} #eosub--get_table_ddl
-# Extend superclass method to create Oracle Text indexes if index type
+# Extend superclass method to create Oracle Text indexes if index type
# is FULLTEXT from schema. Returns a "create index" SQL statement.
sub _get_create_index_ddl {
my ($self, $table_name, $index_name, $index_fields, $index_type) = @_;
$index_name = "idx_" . $self->_hash_identifier($index_name);
if ($index_type eq 'FULLTEXT') {
- my $sql = "CREATE INDEX $index_name ON $table_name ("
+ my $sql = "CREATE INDEX $index_name ON $table_name ("
. join(',',@$index_fields)
. ") INDEXTYPE IS CTXSYS.CONTEXT "
. " PARAMETERS('LEXER BZ_LEX SYNC(ON COMMIT)')" ;
return $sql;
}
- return($self->SUPER::_get_create_index_ddl($table_name, $index_name,
+ return($self->SUPER::_get_create_index_ddl($table_name, $index_name,
$index_fields, $index_type));
}
return $self->SUPER::get_drop_index_ddl($table, $name);
}
-# Oracle supports the use of FOREIGN KEY integrity constraints
+# Oracle supports the use of FOREIGN KEY integrity constraints
# to define the referential integrity actions, including:
# - Update and delete No Action (default)
# - Delete CASCADE
my $to_column = $references->{COLUMN};
my $fk_name = "${table}_${column}_${to_table}_${to_column}";
$fk_name = "fk_" . $self->_hash_identifier($fk_name);
-
+
return $fk_name;
}
}
else {
@sql = $self->SUPER::get_add_column_ddl(@_);
- # Create triggers to deal with empty string.
+ # Create triggers to deal with empty string.
if ($definition->{TYPE} =~ /varchar|TEXT/i && $definition->{NOTNULL}) {
push(@sql, _get_notnull_trigger_ddl($table, $column));
}
# If the types have changed, we have to deal with that.
if (uc(trim($old_def->{TYPE})) ne uc(trim($new_def->{TYPE}))) {
- push(@statements, $self->_get_alter_type_sql($table, $column,
+ push(@statements, $self->_get_alter_type_sql($table, $column,
$new_def, $old_def));
}
. " DEFAULT NULL");
}
# If we went from no default to a default, or we changed the default.
- elsif ( (defined $default && !defined $default_old) ||
- ($default ne $default_old) )
+ elsif ( (defined $default && !defined $default_old) ||
+ ($default ne $default_old) )
{
push(@statements, "ALTER TABLE $table MODIFY $column "
. " DEFAULT $default");
my $setdefault;
# Handle any fields that were NULL before, if we have a default,
$setdefault = $default if defined $default;
- # But if we have a set_nulls_to, that overrides the DEFAULT
- # (although nobody would usually specify both a default and
+ # But if we have a set_nulls_to, that overrides the DEFAULT
+ # (although nobody would usually specify both a default and
# a set_nulls_to.)
$setdefault = $set_nulls_to if defined $set_nulls_to;
if (defined $setdefault) {
push(@statements, "ALTER TABLE $table MODIFY $column"
. " NULL");
push(@statements, "DROP TRIGGER ${table}_${column}")
- if $new_def->{TYPE} =~ /varchar|text/i
+ if $new_def->{TYPE} =~ /varchar|text/i
&& $old_def->{TYPE} =~ /varchar|text/i;
}
my @statements;
my $type = $new_def->{TYPE};
- $type = $self->{db_specific}->{$type}
+ $type = $self->{db_specific}->{$type}
if exists $self->{db_specific}->{$type};
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
- die("You cannot specify a DEFAULT on a SERIAL-type column.")
+ die("You cannot specify a DEFAULT on a SERIAL-type column.")
if $new_def->{DEFAULT};
}
- if ( ($old_def->{TYPE} =~ /LONGTEXT/i && $new_def->{TYPE} !~ /LONGTEXT/i)
+ if ( ($old_def->{TYPE} =~ /LONGTEXT/i && $new_def->{TYPE} !~ /LONGTEXT/i)
|| ($old_def->{TYPE} !~ /LONGTEXT/i && $new_def->{TYPE} =~ /LONGTEXT/i)
) {
- # LONG to VARCHAR or VARCHAR to LONG is not allowed in Oracle,
+ # LONG to VARCHAR or VARCHAR to LONG is not allowed in Oracle,
# just a way to work around.
# Determine whether column_temp is already exist.
my $dbh=Bugzilla->dbh;
my $column_exist = $dbh->selectcol_arrayref(
- "SELECT CNAME FROM COL WHERE TNAME = UPPER(?) AND
+ "SELECT CNAME FROM COL WHERE TNAME = UPPER(?) AND
CNAME = UPPER(?)", undef,$table,$column . "_temp");
if(!@$column_exist) {
- push(@statements,
- "ALTER TABLE $table ADD ${column}_temp $type");
+ push(@statements,
+ "ALTER TABLE $table ADD ${column}_temp $type");
}
push(@statements, "UPDATE $table SET ${column}_temp = $column");
push(@statements, "COMMIT");
push(@statements, "ALTER TABLE $table DROP COLUMN $column");
- push(@statements,
+ push(@statements,
"ALTER TABLE $table RENAME COLUMN ${column}_temp TO $column");
- } else {
+ } else {
push(@statements, "ALTER TABLE $table MODIFY $column $type");
}
push(@statements, "DROP SEQUENCE ${table}_${column}_SEQ");
push(@statements, "DROP TRIGGER ${table}_${column}_TR");
}
-
+
# If this column is changed to type TEXT/VARCHAR, we need to deal with
# empty string.
- if ( $old_def->{TYPE} !~ /varchar|text/i
- && $new_def->{TYPE} =~ /varchar|text/i
+ if ( $old_def->{TYPE} !~ /varchar|text/i
+ && $new_def->{TYPE} =~ /varchar|text/i
&& $new_def->{NOTNULL} )
{
push (@statements, _get_notnull_trigger_ddl($table, $column));
- }
+ }
# If this column is no longer TEXT/VARCHAR, we need to drop the trigger
# that went along with it.
if ( $old_def->{TYPE} =~ /varchar|text/i
&& $new_def->{TYPE} !~ /varchar|text/i )
{
push(@statements, "DROP TRIGGER ${table}_${column}");
- }
+ }
return @statements;
}
return $serial_sql;
}
-sub get_set_serial_sql {
- my ($self, $table, $column, $value) = @_;
+sub get_set_serial_sql {
+ my ($self, $table, $column, $value) = @_;
my @sql;
my $seq_name = "${table}_${column}_SEQ";
push(@sql, "DROP SEQUENCE ${seq_name}");
- push(@sql, $self->_get_create_seq_ddl($table, $column, $value));
+ push(@sql, $self->_get_create_seq_ddl($table, $column, $value));
return @sql;
-}
+}
1;
if ($self->{schema}{$table}{INDEXES}) {
foreach my $index (@{ $self->{schema}{$table}{INDEXES} }) {
if (ref($index) eq 'HASH') {
- delete($index->{TYPE}) if (exists $index->{TYPE}
+ delete($index->{TYPE}) if (exists $index->{TYPE}
&& $index->{TYPE} eq 'FULLTEXT');
}
}
foreach my $index (@{ $self->{abstract_schema}{$table}{INDEXES} }) {
if (ref($index) eq 'HASH') {
- delete($index->{TYPE}) if (exists $index->{TYPE}
+ delete($index->{TYPE}) if (exists $index->{TYPE}
&& $index->{TYPE} eq 'FULLTEXT');
}
}
$self->{db_specific} = {
BOOLEAN => 'smallint',
- FALSE => '0',
+ FALSE => '0',
TRUE => '1',
INT1 => 'integer',
my $def = $self->get_column_abstract($table, $old_name);
if ($def->{TYPE} =~ /SERIAL/i) {
# We have to rename the series also.
- push(@sql, "ALTER SEQUENCE ${table}_${old_name}_seq
+ push(@sql, "ALTER SEQUENCE ${table}_${old_name}_seq
RENAME TO ${table}_${new_name}_seq");
}
return @sql;
my @statements;
my $type = $new_def->{TYPE};
- $type = $self->{db_specific}->{$type}
+ $type = $self->{db_specific}->{$type}
if exists $self->{db_specific}->{$type};
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
- die("You cannot specify a DEFAULT on a SERIAL-type column.")
+ die("You cannot specify a DEFAULT on a SERIAL-type column.")
if $new_def->{DEFAULT};
}
push(@statements, "SELECT setval('${table}_${column}_seq',
MAX($table.$column))
FROM $table");
- push(@statements, "ALTER TABLE $table ALTER COLUMN $column
+ push(@statements, "ALTER TABLE $table ALTER COLUMN $column
SET DEFAULT nextval('${table}_${column}_seq')");
}
# If this column is no longer SERIAL, we need to drop the sequence
# that went along with it.
if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i) {
- push(@statements, "ALTER TABLE $table ALTER COLUMN $column
+ push(@statements, "ALTER TABLE $table ALTER COLUMN $column
DROP DEFAULT");
- push(@statements, "ALTER SEQUENCE ${table}_${column}_seq
+ push(@statements, "ALTER SEQUENCE ${table}_${column}_seq
OWNED BY NONE");
push(@statements, "DROP SEQUENCE ${table}_${column}_seq");
}
$self->{db_specific} = {
BOOLEAN => 'integer',
- FALSE => '0',
+ FALSE => '0',
TRUE => '1',
INT1 => 'integer',
# This does most of the "heavy lifting" of the schema-altering functions.
sub _sqlite_alter_schema {
my ($self, $table, $create_table, $options) = @_;
-
+
# $create_table is sometimes an array in the form that _sqlite_table_lines
# returns.
if (ref $create_table) {
$create_table = join(',', @$create_table) . "\n)";
}
-
+
my $dbh = Bugzilla->dbh;
-
+
my $random = generate_random_password(5);
my $rename_to = "${table}_$random";
@insert_cols = map { $_ eq $from ? $to : $_ } @insert_cols;
}
}
-
+
my $insert_str = join(',', @insert_cols);
my $select_str = join(',', @select_cols);
my $copy_sql = "INSERT INTO $table ($insert_str)"
. " SELECT $select_str FROM $rename_to";
-
+
# We have to turn FKs off before doing this. Otherwise, when we rename
# the table, all of the FKs in the other tables will be automatically
# updated to point to the renamed table. Note that PRAGMA foreign_keys
"DROP TABLE $rename_to",
'COMMIT TRANSACTION',
'PRAGMA foreign_keys = ON',
- );
+ );
}
# For finding a particular column's definition in a CREATE TABLE statement.
sub get_type_ddl {
my $self = shift;
my $def = dclone($_[0]);
-
+
my $ddl = $self->SUPER::get_type_ddl(@_);
if ($def->{PRIMARYKEY} and $def->{TYPE} =~ /SERIAL/i) {
$ddl =~ s/\bSERIAL\b/integer/;
my $self = shift;
my ($table, $column, $new_def, $set_nulls_to) = @_;
my $dbh = Bugzilla->dbh;
-
+
my $table_sql = $self->_sqlite_create_table($table);
my $new_ddl = $self->get_type_ddl($new_def);
# When we do ADD COLUMN, columns can show up all on one line separated
return $self->_sqlite_alter_schema($table, $table_sql,
{ pre_sql => \@pre_sql, extra_column => $column });
}
-
+
return $self->SUPER::get_add_column_ddl(@_);
}
my ($self, $table, $column, $references) = @_;
my @clauses = $self->_sqlite_table_lines($table);
my $fk_name = $self->_get_fk_name($table, $column, $references);
-
+
my $line_re = qr/^\s+CONSTRAINT $fk_name /s;
grep { $line_re } @clauses
or die "Can't find $fk_name: " . join(',', @clauses);
@clauses = grep { $_ !~ $line_re } @clauses;
-
+
return $self->_sqlite_alter_schema($table, \@clauses);
}
sub new {
my ($class, $params) = @_;
my $db_name = $params->{db_name};
-
+
# Let people specify paths intead of data/ for the DB.
if ($db_name and $db_name !~ m{[\\/]}) {
# When the DB is first created, there's a chance that the
sqlite_unicode => Bugzilla->params->{'utf8'},
};
- my $self = $class->db_new({ dsn => $dsn, user => '',
+ my $self = $class->db_new({ dsn => $dsn, user => '',
pass => '', attrs => $attrs });
# Needed by TheSchwartz
$self->{private_bz_dsn} = $dsn;
-
+
my %pragmas = (
# Make sure that the sqlite file doesn't grow without bound.
auto_vacuum => 1,
# uncomment this line.
#journal_mode => "'WAL'",
);
-
+
while (my ($name, $value) = each %pragmas) {
$self->do("PRAGMA $name = $value");
}
-
+
$self->sqlite_create_collation('bugzilla', \&_sqlite_collate_ci);
$self->sqlite_create_function('position', 2, \&_sqlite_position);
$self->sqlite_create_function('iposition', 2, \&_sqlite_position_ci);
number_of_shards => 2,
analysis => {
filter => {
- asciifolding_original => {
+ asciifolding_original => {
type => "asciifolding",
preserve_original => \1,
},
sub _build__order {
my ($self) = @_;
-
+
my @order;
foreach my $order (@{$self->_input_order}) {
if ($order =~ /^(.+)\s+(asc|desc)$/i) {
sub _describe {
my ($thing) = @_;
-
+
state $class_to_func = {
'Bugzilla::Search::Condition' => \&_describe_condition,
'Bugzilla::Search::Clause' => \&_describe_clause
with 'Throwable';
has 'redirect_args' => (is => 'ro', required => 1);
-
+
package Bugzilla::Elastic::Search::UnsupportedField;
use Moo;
use overload q{""} => sub { "Unsupported field: ", $_[0]->field }, fallback => 1;
has 'field' => (is => 'ro', required => 1);
-
+
package Bugzilla::Elastic::Search::UnsupportedOperator;
use Moo;
my $type = $1 // 'unknown';
die Template::Exception->new("bugzilla.$type.$error", $vars);
}
-
+
if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) {
if (sentry_should_notify($vars->{error})) {
$vars->{maintainers_notified} = 1;
my ($class, $extension_file, $config_file) = @_;
my $package;
- # This is needed during checksetup.pl, because Extension packages can
+ # This is needed during checksetup.pl, because Extension packages can
# only be loaded once (they return "1" the second time they're loaded,
# instead of their name). During checksetup.pl, extensions are loaded
# once by Bugzilla::Install::Requirements, and then later again via
my $name = require $config_file;
if ($name =~ /^\d+$/) {
ThrowCodeError('extension_must_return_name',
- { extension => $config_file,
+ { extension => $config_file,
returned => $name });
}
$package = "${class}::$name";
else {
my $name = require $extension_file;
if ($name =~ /^\d+$/) {
- ThrowCodeError('extension_must_return_name',
+ ThrowCodeError('extension_must_return_name',
{ extension => $extension_file, returned => $name });
}
$package = "${class}::$name";
}
if (!eval { $package->NAME }) {
- ThrowCodeError('extension_no_name',
+ ThrowCodeError('extension_no_name',
{ filename => $extension_file, package => $package });
}
=head1 SYNOPSIS
-The following would be in F<extensions/Foo/Extension.pm> or
+The following would be in F<extensions/Foo/Extension.pm> or
F<extensions/Foo.pm>:
package Bugzilla::Extension::Foo
__PACKAGE__->NAME;
Custom templates would go into F<extensions/Foo/template/en/default/>.
-L<Template hooks|/Template Hooks> would go into
+L<Template hooks|/Template Hooks> would go into
F<extensions/Foo/template/en/default/hook/>.
=head1 DESCRIPTION
The L</SYNOPSIS> above gives a pretty good overview of what's basically
required to write an extension. This section gives more information
-on exactly how extensions work and how you write them. There is also a
+on exactly how extensions work and how you write them. There is also a
L<wiki page|https://wiki.mozilla.org/Bugzilla:Extension_Notes> with additional HOWTOs, tips and tricks.
=head2 Using F<extensions/create.pl>
=item 1
If your extension will have only code and no templates or other files,
-you can create a simple C<.pm> file in the F<extensions/> directory.
+you can create a simple C<.pm> file in the F<extensions/> directory.
For example, if you wanted to create an extension called "Foo" using this
method, you would put your code into a file called F<extensions/Foo.pm>.
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
+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
+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,
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
+which has the same format as
L<Bugzilla::Install::Requirements/OPTIONAL_MODULES>.
=head3 If Your Extension Needs Certain Modules In Order To Compile
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
+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
+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>,
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
+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.
=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
+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
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
+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
+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.
=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
+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
+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
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
+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<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
=item <templates>
-This is the full path to the template directory, like
+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
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>
+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
+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 <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
+of the template you want to hook. In our example, this is
C<additional_header>.
=item <template type>
=head3 Adding New Template Hooks to Bugzilla
-Adding new template hooks is just like adding code hooks (see
+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.
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
+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.
+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
If you include CSS, JavaScript, and images in your extension that are
served directly to the user (that is, they're not read by a script and
then printed--they're just linked directly in your HTML), they should go
-into the F<web/> subdirectory of your extension.
+into the F<web/> subdirectory of your extension.
So, for example, if you had a CSS file called F<style.css> and your
-extension was called F<Foo>, your file would go into
+extension was called F<Foo>, your file would go into
F<extensions/Foo/web/style.css>.
=head2 Disabling Your Extension
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
+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
+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
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>,
+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
=head3 C<package_dir>
-This returns the directory that your extension is located in.
+This returns the directory that your extension is located in.
-If this is an extension that was installed via CPAN, the directory will
+If this is an extension that was installed via CPAN, the directory will
be the path to F<Bugzilla/Extension/Foo/>, if C<Foo.pm> is the name of your
extension.
use constant SQL_DEFINITIONS => {
# Using commas because these are constants and they shouldn't
# be auto-quoted by the "=>" operator.
- FIELD_TYPE_FREETEXT, { TYPE => 'varchar(255)',
+ FIELD_TYPE_FREETEXT, { TYPE => 'varchar(255)',
NOTNULL => 1, DEFAULT => "''"},
FIELD_TYPE_SINGLE_SELECT, { TYPE => 'varchar(64)', NOTNULL => 1,
DEFAULT => "'---'" },
- FIELD_TYPE_TEXTAREA, { TYPE => 'MEDIUMTEXT',
+ FIELD_TYPE_TEXTAREA, { TYPE => 'MEDIUMTEXT',
NOTNULL => 1, DEFAULT => "''"},
FIELD_TYPE_DATETIME, { TYPE => 'DATETIME' },
FIELD_TYPE_DATE, { TYPE => 'DATE' },
($name =~ $name_regex && $name ne "cf_")
|| ThrowUserError('field_invalid_name', { name => $name });
- # If it's custom, prepend cf_ to the custom field name to distinguish
+ # If it's custom, prepend cf_ to the custom field name to distinguish
# it from standard fields.
if ($name !~ /^cf_/ && $params->{custom}) {
$name = 'cf_' . $name;
if ($type != FIELD_TYPE_BUG_ID) {
return undef; # store NULL for non-reversible field types
}
-
+
$reverse_desc = clean_text($reverse_desc);
return $reverse_desc;
}
=item C<enter_bug>
-A boolean specifying whether or not this field should appear on
+A boolean specifying whether or not this field should appear on
enter_bug.cgi
=back
sub is_select {
my ($invocant, $params) = @_;
# This allows this method to be called by create() validators.
- my $type = blessed($invocant) ? $invocant->type : $params->{type};
- return ($type == FIELD_TYPE_SINGLE_SELECT
- || $type == FIELD_TYPE_MULTI_SELECT) ? 1 : 0
+ my $type = blessed($invocant) ? $invocant->type : $params->{type};
+ return ($type == FIELD_TYPE_SINGLE_SELECT
+ || $type == FIELD_TYPE_MULTI_SELECT) ? 1 : 0
}
=over
sub visibility_field {
my $self = shift;
if ($self->{visibility_field_id}) {
- $self->{visibility_field} ||=
+ $self->{visibility_field} ||=
$self->new({ id => $self->{visibility_field_id}, cache => 1 });
}
return $self->{visibility_field};
sub visibility_values {
my $self = shift;
my $dbh = Bugzilla->dbh;
-
+
return [] if !$self->{visibility_field_id};
if (!defined $self->{visibility_values}) {
sub controls_visibility_of {
my $self = shift;
- $self->{controls_visibility_of} ||=
+ $self->{controls_visibility_of} ||=
Bugzilla::Field->match({ visibility_field_id => $self->id });
return $self->{controls_visibility_of};
}
=cut
-sub is_relationship {
+sub is_relationship {
my $self = shift;
my $desc = $self->reverse_desc;
if (defined $desc && $desc ne "") {
# This field has to be created separately, or the above upgrade code
# might not run properly.
- Bugzilla::Field->create({ name => $new_field_name,
+ Bugzilla::Field->create({ name => $new_field_name,
description => $field_description })
unless new Bugzilla::Field({ name => $new_field_name });
my $dbh = Bugzilla->dbh;
# If $legalsRef is undefined, we use the default valid values.
- # Valid values for this check are all possible values.
+ # Valid values for this check are all possible values.
# Using get_legal_values would only return active values, but since
- # some bugs may have inactive values set, we want to check them too.
+ # some bugs may have inactive values set, we want to check them too.
unless (defined $legalsRef) {
$legalsRef = Bugzilla::Field->new({name => $name})->legal_values;
my @values = map($_->name, @$legalsRef);
# Constructors #
################
-# We just make new() enforce this, which should give developers
+# We just make new() enforce this, which should give developers
# the understanding that you can't use Bugzilla::Field::Choice
# without calling type().
sub new {
});
}
if ($self->is_static) {
- ThrowUserError('fieldvalue_not_deletable',
+ ThrowUserError('fieldvalue_not_deletable',
{ field => $self->field, value => $self });
}
if ($self->bug_count) {
if (!$value and ref $invocant) {
if ($invocant->is_default) {
my $field = $invocant->field;
- ThrowUserError('fieldvalue_is_default',
+ ThrowUserError('fieldvalue_is_default',
{ value => $invocant, field => $field,
param_name => $invocant->DEFAULT_MAP->{$field->name}
});
$value = trim($value);
# Make sure people don't rename static values
- if (blessed($invocant) && $value ne $invocant->name
- && $invocant->is_static)
+ if (blessed($invocant) && $value ne $invocant->name
+ && $invocant->is_static)
{
ThrowUserError('fieldvalue_not_editable',
{ field => $field, old_value => $invocant });
my $exists = $invocant->type($field)->new({ name => $value });
if ($exists && (!blessed($invocant) || $invocant->id != $exists->id)) {
- ThrowUserError('fieldvalue_already_exists',
+ ThrowUserError('fieldvalue_already_exists',
{ field => $field, value => $exists });
}
my $choices = Bugzilla::Field::Choice->type($field)->new_from_list([1,2,3]);
my $choices = Bugzilla::Field::Choice->type($field)->get_all();
- my $choices = Bugzilla::Field::Choice->type($field->match({ sortkey => 10 });
+ my $choices = Bugzilla::Field::Choice->type($field->match({ sortkey => 10 });
=head1 DESCRIPTION
This is an implementation of L<Bugzilla::Object>, but with a twist.
-You can't call any class methods (such as C<new>, C<create>, etc.)
+You can't call any class methods (such as C<new>, C<create>, etc.)
directly on C<Bugzilla::Field::Choice> itself. Instead, you have to
call C<Bugzilla::Field::Choice-E<gt>type($field)> to get the class
you're going to instantiate, and then you call the methods on that.
WHERE value = ?", undef, $self->name);
}
else {
- $count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs
+ $count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs
WHERE $fname = ?",
undef, $self->name);
}
my $cache = Bugzilla->request_cache;
# This is just to make life easier for subclasses. Our auto-generated
# subclasses from Bugzilla::Field::Choice->type() already have this set.
- $cache->{"field_$class"} ||=
+ $cache->{"field_$class"} ||=
new Bugzilla::Field({ name => $class->FIELD_NAME });
return $cache->{"field_$class"};
}
my %controlled_values;
require Bugzilla::Field::Choice;
foreach my $field (@$fields) {
- $controlled_values{$field->name} =
+ $controlled_values{$field->name} =
Bugzilla::Field::Choice->type($field)
->match({ visibility_value_id => $self->id });
}
# Values with a visibility value are only shown if the visibility
# value is set on the bug.
- return $visibility_value->is_set_on_bug($bug);
+ return $visibility_value->is_set_on_bug($bug);
}
sub is_set_on_bug {
my ($self, $bug) = @_;
my $field_name = $self->FIELD_NAME;
- # This allows bug/create/create.html.tmpl to pass in a hashref that
+ # This allows bug/create/create.html.tmpl to pass in a hashref that
# looks like a bug object.
my $value = blessed($bug) ? $bug->$field_name : $bug->{$field_name};
$value = $value->name if blessed($value);
Returns a hashref of arrayrefs. The hash keys are the names of fields,
and the values are arrays of objects that implement
-C<Bugzilla::Field::ChoiceInterface>, representing values that this value
+C<Bugzilla::Field::ChoiceInterface>, representing values that this value
controls the visibility of, for that field.
=item C<visibility_value>
=item C<is_visible_on_bug>
-Returns C<1> if, according to the settings of C<is_active> and
+Returns C<1> if, according to the settings of C<is_active> and
C<visibility_value>, this value should be displayed as an option
when viewing a bug. Returns C<0> otherwise.
attach_id
requestee_id
setter_id
- status),
+ status),
$dbh->sql_date_format('creation_date', '%Y.%m.%d %H:%i:%s') .
- ' AS creation_date',
+ ' AS creation_date',
$dbh->sql_date_format('modification_date', '%Y.%m.%d %H:%i:%s') .
' AS modification_date';
}
# Make sure the user can change flags
my $privs;
$bug->check_can_change_field('flagtypes.name', 0, 1, \$privs)
- || ThrowUserError('illegal_change',
+ || ThrowUserError('illegal_change',
{ field => 'flagtypes.name', privs => $privs });
# Update (or delete) an existing flag.
=item C<sqlify_criteria($criteria, $tables)>
Converts a hash of criteria into a list of SQL criteria.
-$criteria is a reference to the criteria (field => value),
-$tables is a reference to an array of tables being accessed
+$criteria is a reference to the criteria (field => value),
+$tables is a reference to an array of tables being accessed
by the query.
=back
push(@criteria, "flagtypes.is_active = $is_active");
}
if (exists($criteria->{active_or_has_flags}) && $criteria->{active_or_has_flags} =~ /^\d+$/) {
- push(@$tables, "LEFT JOIN flags AS f ON flagtypes.id = f.type_id " .
+ push(@$tables, "LEFT JOIN flags AS f ON flagtypes.id = f.type_id " .
"AND f.bug_id = " . $criteria->{active_or_has_flags});
- push(@criteria, "(flagtypes.is_active = 1 OR f.id IS NOT NULL)");
+ push(@criteria, "(flagtypes.is_active = 1 OR f.id IS NOT NULL)");
}
if ($criteria->{product_id}) {
my $product_id = $criteria->{product_id};
# by flag type ID and target product/component.
push(@$tables, "INNER JOIN flaginclusions AS i ON flagtypes.id = i.type_id");
push(@criteria, "(i.product_id = $product_id OR i.product_id IS NULL)");
-
+
# Add exclusions to the query, which is more complicated. First of all,
# we do a LEFT JOIN so we don't miss flag types with no exclusions.
# Then, as with inclusions, we join on flag type ID and target product/
sub grant_direct {
my ($self, $type) = @_;
$self->{grant_direct} ||= {};
- return $self->{grant_direct}->{$type}
+ return $self->{grant_direct}->{$type}
if defined $self->{grant_direct}->{$type};
my $dbh = Bugzilla->dbh;
my $ids = $dbh->selectcol_arrayref(
"SELECT member_id FROM group_group_map
- WHERE grantor_id = ? AND grant_type = $type",
+ WHERE grantor_id = ? AND grant_type = $type",
undef, $self->id) || [];
$self->{grant_direct}->{$type} = $self->new_from_list($ids);
$self->id);
my @ids = map { $_->{product_id} } @$product_data;
require Bugzilla::Product;
- my $products = Bugzilla::Product->new_from_list(\@ids);
+ my $products = Bugzilla::Product->new_from_list(\@ids);
my %data_map = map { $_->{product_id} => $_ } @$product_data;
my @retval;
foreach my $product (@$products) {
}
# If we've changed this group to be active, fix any Mandatory groups.
- $self->_enforce_mandatory if (exists $changes->{isactive}
+ $self->_enforce_mandatory if (exists $changes->{isactive}
&& $changes->{isactive}->[1]);
$self->_rederive_regexp() if exists $changes->{userregexp};
- Bugzilla::Hook::process('group_end_of_update',
+ Bugzilla::Hook::process('group_end_of_update',
{ group => $self, changes => $changes });
$dbh->bz_commit_transaction();
Bugzilla->memcached->clear_config();
if (scalar(@$bugs) && !$params->{'remove_from_bugs'}) {
$cantdelete = 1;
}
-
+
my $products = $self->products;
if (scalar(@$products) && !$params->{'remove_from_products'}) {
$cantdelete = 1;
my @groupidstocheck = @groups;
my %groupidschecked = ();
$sth = $dbh->prepare("SELECT member_id FROM group_group_map
- WHERE grantor_id = ?
+ WHERE grantor_id = ?
AND grant_type = " . GROUP_MEMBERSHIP);
while (my $node = shift @groupidstocheck) {
$sth->execute($node);
=item C<check_members_are_visible>
-Throws an error if this group is not visible (according to
+Throws an error if this group is not visible (according to
visibility groups) to the currently-logged-in user.
=item C<check_remove>
=item C<flatten_group_membership>
-Accepts a list of groups and returns a list of all the groups whose members
+Accepts a list of groups and returns a list of all the groups whose members
inherit membership in any group on the list. So, we can determine if a user
is in any of the groups input to flatten_group_membership by querying the
user_group_map for any user with DIRECT or REGEXP membership IN() the list
=head1 DESCRIPTION
-Bugzilla allows extension modules to drop in and add routines at
+Bugzilla allows extension modules to drop in and add routines at
arbitrary points in Bugzilla code. These points are referred to as
hooks. When a piece of standard Bugzilla code wants to allow an extension
to perform additional functions, it uses Bugzilla::Hook's L</process>
-subroutine to invoke any extension code if installed.
+subroutine to invoke any extension code if installed.
The implementation of extensions is described in L<Bugzilla::Extension>.
=item C<$name> - The name of the hook to invoke.
-=item C<$args> - A hashref. The named args to pass to the hook.
+=item C<$args> - A hashref. The named args to pass to the hook.
They will be passed as arguments to the hook method in the extension.
=back
=item C<modules>
This is a hash--a mapping from login-type "names" to the actual module on
-disk. The keys will be all the values that were passed to
+disk. The keys will be all the values that were passed to
L<Bugzilla::Auth/login> for the C<Login> parameter. The values are the
actual path to the module on disk. (For example, if the key is C<DB>, the
value is F<Bugzilla/Auth/Login/DB.pm>.)
-For your extension, the path will start with
-F<Bugzilla/Extension/Foo/>, where "Foo" is the name of your Extension.
+For your extension, the path will start with
+F<Bugzilla/Extension/Foo/>, where "Foo" is the name of your Extension.
(See the code in the example extension.)
If your login type is in the hash as a key, you should set that key to the
=over
-=item C<bug>
+=item C<bug>
The changed bug object, with all fields set to their updated values.
A bug object pulled from the database before the fields were set to
their updated values (so it has the old values available for each field).
-=item C<timestamp>
+=item C<timestamp>
The timestamp used for all updates in this transaction, as a SQL date
string.
-=item C<changes>
+=item C<changes>
The hash of changed fields. C<< $changes->{field} = [old, new] >>
=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.
+here for site-specific policy changes and other customizations.
-This hook is only executed if the field's new and old values differ.
+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
You should push a hashref containing two keys (C<match> and C<replace>)
in to this array. C<match> is the regular expression that matches the
text you want to replace, C<replace> is what you want to replace that
-text with. (This gets passed into a regular expression like
+text with. (This gets passed into a regular expression like
C<s/$match/$replace/>.)
Instead of specifying a regular expression for C<replace> you can also
A B<reference> to the exact text that you are parsing.
-Generally you should not modify this yourself. Instead you should be
+Generally you should not modify this yourself. Instead you should be
returning regular expressions using the C<regexes> array.
The text has not been parsed in any way. (So, for example, it is not
=over
-=item C<bug>
+=item C<bug>
The changed bug object, with all fields set to their updated values.
A bug object pulled from the database before the fields were set to
their updated values (so it has the old values available for each field).
-=item C<timestamp>
+=item C<timestamp>
The timestamp used for all updates in this transaction, as a SQL date
string.
-=item C<changes>
+=item C<changes>
The hash of changed fields. C<< $changes->{field} = [old, new] >>
=over
=item C<column_joins> - A hashref containing data to return back to
-L<Bugzilla::Search>. This hashref contains names of the columns as keys and
+L<Bugzilla::Search>. This hashref contains names of the columns as keys and
a hashref about table to join as values. This hashref has the following keys:
=over
This allows you to modify L<Bugzilla::Search/OPERATOR_FIELD_OVERRIDE>,
which determines the search functions for fields. It allows you to specify
-custom search functionality for certain fields.
+custom search functionality for certain fields.
See L<Bugzilla::Search/OPERATOR_FIELD_OVERRIDE> for reference and see
the code in the example extension.
This is a hash of L<Bugzilla::User> objects, keyed by id. This is so you can
find out more information about any of the user ids in the C<recipients> hash.
-Every id in the incoming C<recipients> hash will have an object in here.
-(But if you add additional recipients to the C<recipients> hash, you are
+Every id in the incoming C<recipients> hash will have an object in here.
+(But if you add additional recipients to the C<recipients> hash, you are
B<not> required to add them to this hash.)
=item C<diffs>
-This is a list of hashes, each hash representing a change to the bug. Each
-hash has the following members: C<field_name>, C<bug_when>, C<old>, C<new>
-and C<who> (a L<Bugzilla::User>). If appropriate, there will also be
-C<attach_id> or C<comment_id>; if either is present, there will be
-C<isprivate>. See C<_get_diffs> in F<Bugzilla/BugMail.pm> to see exactly how
-it is populated. Warning: the format and existence of the "diffs" parameter
+This is a list of hashes, each hash representing a change to the bug. Each
+hash has the following members: C<field_name>, C<bug_when>, C<old>, C<new>
+and C<who> (a L<Bugzilla::User>). If appropriate, there will also be
+C<attach_id> or C<comment_id>; if either is present, there will be
+C<isprivate>. See C<_get_diffs> in F<Bugzilla/BugMail.pm> to see exactly how
+it is populated. Warning: the format and existence of the "diffs" parameter
is subject to change in future releases of Bugzilla.
=back
is the Perl module representing that panel. For example, if
the name is C<Auth>, the value would be C<Bugzilla::Config::Auth>.
-For your extension, the Perl module would start with
+For your extension, the Perl module would start with
C<Bugzilla::Extension::Foo>, where "Foo" is the name of your Extension.
(See the code in the example extension.)
=item C<panels>
-A hashref, where the keys are lower-case panel "names" (like C<auth>,
+A hashref, where the keys are lower-case panel "names" (like C<auth>,
C<admin>, etc.) and the values are hashrefs. The hashref contains a
single key, C<params>. C<params> is an arrayref--the return value from
C<get_param_list> for that module. You can modify C<params> and
=over
-=item C<group>
+=item C<group>
The new L<Bugzilla::Group> object that was just created.
=head2 group_end_of_update
-This happens at the end of L<Bugzilla::Group/update>, after all other
+This happens at the end of L<Bugzilla::Group/update>, after all other
changes are made to the database. This occurs inside a database transaction.
Params:
=over
-=item C<group> - The changed L<Bugzilla::Group> object, with all fields set
+=item C<group> - The changed L<Bugzilla::Group> object, with all fields set
to their updated values.
-=item C<changes> - The hash of changed fields.
+=item C<changes> - The hash of changed fields.
C<< $changes->{$field} = [$old, $new] >>
=back
=head2 install_before_final_checks
-Allows execution of custom code before the final checks are done in
+Allows execution of custom code before the final checks are done in
checksetup.pl.
Params:
Allows for additional files and directories to be added to the
list of files and directories already managed by checksetup.pl.
You will be able to also set permissions for the files and
-directories using this hook. You can also use this hook to create
+directories using this hook. You can also use this hook to create
appropriate .htaccess files for any directory to secure its contents.
For examples see L<FILESYSTEM> in L<Bugzilla::Install::Filesystem>.
=item C<recurse_dirs>
-Hash reference of directories that will have permissions set for each item inside
+Hash reference of directories that will have permissions set for each item inside
each of the directories, including the directory itself. Each directory key
points to another hash reference containing the following settings.
=item C<htaccess>
Hash reference containing htaccess files to be created. You can set the permissions
-for the htaccess as well as the contents of the file. Each file key points to another
+for the htaccess as well as the contents of the file. Each file key points to another
hash reference containing the following settings.
Params:
=head2 db_schema_abstract_schema
-This allows you to add tables to Bugzilla. Note that we recommend that you
+This allows you to add tables to Bugzilla. Note that we recommend that you
prefix the names of your tables with some word (preferably the name of
your Extension), so that they don't conflict with any future Bugzilla tables.
=over
-=item C<schema> - A hashref, in the format of
+=item C<schema> - A hashref, in the format of
L<Bugzilla::DB::Schema/ABSTRACT_SCHEMA>. Add new hash keys to make new table
definitions. F<checksetup.pl> will automatically add these tables to the
database when run.
=head2 job_map
-Bugzilla has a system - L<Bugzilla::JobQueue> - for running jobs
-asynchronously, if the administrator has set it up. This hook allows the
-addition of mappings from job names to handler classes, so an extension can
+Bugzilla has a system - L<Bugzilla::JobQueue> - for running jobs
+asynchronously, if the administrator has set it up. This hook allows the
+addition of mappings from job names to handler classes, so an extension can
fire off jobs.
Params:
=over
-=item C<job_map> - The job map hash. Key: the name of the job, as should be
-passed to Bugzilla->job_queue->insert(). Value: the name of the Perl module
-which implements the task (an instance of L<TheSchwartz::Worker>).
+=item C<job_map> - The job map hash. Key: the name of the job, as should be
+passed to Bugzilla->job_queue->insert(). Value: the name of the Perl module
+which implements the task (an instance of L<TheSchwartz::Worker>).
=back
=item C<class>
-The name of the class that C<create> was called on. You can check this
+The name of the class that C<create> was called on. You can check this
like C<< if ($class->isa('Some::Class')) >> in your code, to perform specific
tasks before C<create> for only certain classes.
The name of the field being updated in the object.
-=item C<value>
+=item C<value>
The value being set on the object.
=item C<class>
-The name of the class that this hook is being called on. You can check this
+The name of the class that this hook is being called on. You can check this
like C<< if ($class->isa('Some::Class')) >> in your code, to add new
fields only for certain classes.
=item C<columns>
An arrayref. Add the string names of columns to this array to add new
-values to objects.
+values to objects.
For example, if you add an C<example> column to a particular table
-(using L</install_update_db>), and then push the string C<example> into
+(using L</install_update_db>), and then push the string C<example> into
this array for the object that uses that table, then you can access the
information in that column via C<< $object->{example} >> on all objects
of that type.
=item C<class>
-The name of the class that C<create> was called on. You can check this
+The name of the class that C<create> was called on. You can check this
like C<< if ($class->isa('Some::Class')) >> in your code, to perform specific
tasks for only certain classes.
=item C<class>
-The name of the class that C<create> was called on. You can check this
+The name of the class that C<create> was called on. You can check this
like C<< if ($class->isa('Some::Class')) >> in your code, to perform specific
tasks for only certain classes.
=item C<class>
-The name of the class that C<VALIDATORS> was called on. You can check this
+The name of the class that C<VALIDATORS> was called on. You can check this
like C<< if ($class->isa('Some::Class')) >> in your code, to add
validators only for certain classes.
(However, note that such rollbacks should not normally be used, as
some databases that Bugzilla supports have very bad rollback performance.
If you want to validate input and throw errors before the Product is created,
-use L</object_end_of_create_validators> instead, or add a validator
+use L</object_end_of_create_validators> instead, or add a validator
using L</object_validators>.)
Params:
=head2 quicksearch_map
-This hook allows you to alter the Quicksearch syntax to include e.g. special
+This hook allows you to alter the Quicksearch syntax to include e.g. special
searches for custom fields you have.
Params:
=over
-=item C<map> - a hash where the key is the name you want to use in
-Quicksearch, and the value is the name from the C<fielddefs> table that you
+=item C<map> - a hash where the key is the name you want to use in
+Quicksearch, and the value is the name from the C<fielddefs> table that you
want it to map to. You can modify existing mappings or add new ones.
=back
in the template. It works exactly as though you inserted code to modify
template variables at the top of a template.
-You probably want to restrict this hook to operating only if a certain
+You probably want to restrict this hook to operating only if a certain
file is being processed (which is why you get a C<file> argument
below). Otherwise, modifying the C<vars> argument will affect every single
template in Bugzilla.
=item C<dispatch>
A hashref where you can specify the names of your modules and which Perl
-module handles the functions for that module. (This is actually sent to
+module handles the functions for that module. (This is actually sent to
L<SOAP::Lite/dispatch_with>. You can see how that's used in F<xmlrpc.cgi>.)
The Perl module name will most likely start with C<Bugzilla::Extension::Foo::>
package Bugzilla::Install;
-# Functions in this this package can assume that the database
+# Functions in this this package can assume that the database
# has been set up, params are available, localconfig is
# available, and any module can be used.
#
if ($inherited_by) {
foreach my $name (@$inherited_by) {
my $member = Bugzilla::Group->check($name);
- $dbh->do('INSERT INTO group_group_map (grantor_id,
+ $dbh->do('INSERT INTO group_group_map (grantor_id,
member_id) VALUES (?,?)',
undef, $created->id, $member->id);
}
# And same for the default product/component.
if (!$dbh->selectrow_array('SELECT 1 FROM products')) {
- print get_text('install_default_product',
+ print get_text('install_default_product',
{ name => DEFAULT_PRODUCT->{name} }) . "\n";
my $product = Bugzilla::Product->create(DEFAULT_PRODUCT);
my $template = Bugzilla->template;
my $admin_group = new Bugzilla::Group({ name => 'admin' });
- my $admin_inheritors =
+ my $admin_inheritors =
Bugzilla::Group->flatten_group_membership($admin_group->id);
my $admin_group_ids = join(',', @$admin_inheritors);
my ($admin_count) = $dbh->selectrow_array(
- "SELECT COUNT(*) FROM user_group_map
+ "SELECT COUNT(*) FROM user_group_map
WHERE group_id IN ($admin_group_ids)");
return if $admin_count;
get_text('install_admin_get_password'));
}
- my $admin = Bugzilla::User->create({ login_name => $login,
+ my $admin = Bugzilla::User->create({ login_name => $login,
realname => $full_name,
cryptpassword => $password });
make_admin($admin);
my ($user) = @_;
my $dbh = Bugzilla->dbh;
- $user = ref($user) ? $user
+ $user = ref($user) ? $user
: new Bugzilla::User(login_to_id($user, THROW_ERROR));
my $group_insert = $dbh->prepare(
my $admin_group = new Bugzilla::Group({ name => 'admin' });
# These are run in an eval so that we can ignore the error of somebody
# already being granted these things.
- eval {
- $group_insert->execute($user->id, $admin_group->id, 0, GRANT_DIRECT);
+ eval {
+ $group_insert->execute($user->id, $admin_group->id, 0, GRANT_DIRECT);
};
eval {
$group_insert->execute($user->id, $admin_group->id, 1, GRANT_DIRECT);
# Admins should also have editusers directly, even though they'll usually
# inherit it. People could have changed their inheritance structure.
my $editusers = new Bugzilla::Group({ name => 'editusers' });
- eval {
- $group_insert->execute($user->id, $editusers->id, 0, GRANT_DIRECT);
+ eval {
+ $group_insert->execute($user->id, $editusers->id, 0, GRANT_DIRECT);
};
# If there is no maintainer set, make this user the maintainer.
package Bugzilla::Install::DB;
# NOTE: This package may "use" any modules that it likes,
-# localconfig is available, and params are up to date.
+# localconfig is available, and params are up to date.
use 5.10.1;
use strict;
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
$dbh->do('UPDATE fielddefs SET is_numeric = 1 WHERE type = '
. FIELD_TYPE_BUG_ID);
-
+
Bugzilla::Hook::process('install_update_db_fielddefs');
# Remember, this is not the function for adding general table changes.
#
# This function runs in historical order--from upgrades that older
# installations need, to upgrades that newer installations need.
-# The order of items inside this function should only be changed if
+# The order of items inside this function should only be changed if
# absolutely necessary.
#
# The subroutines should have long, descriptive names, so that you
# can easily see what is being done, just by reading this function.
#
# This function is mostly self-documenting. If you're curious about
-# what each of the added/removed columns does, you should see the schema
+# what each of the added/removed columns does, you should see the schema
# docs at:
# http://www.ravenbrook.com/project/p4dti/tool/cgi/bugzilla-schema/
#
_update_pre_checksetup_bugzillas();
$dbh->bz_add_column('attachments', 'submitter_id',
- {TYPE => 'INT3', NOTNULL => 1}, 0);
+ {TYPE => 'INT3', NOTNULL => 1}, 0);
$dbh->bz_rename_column('bugs_activity', 'when', 'bug_when');
_add_unique_login_name_index_to_profiles();
- $dbh->bz_add_column('profiles', 'mybugslink',
+ $dbh->bz_add_column('profiles', 'mybugslink',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
_update_component_user_fields_to_ids();
_add_longdescs_already_wrapped();
- # Moved enum types to separate tables so we need change the old enum
+ # Moved enum types to separate tables so we need change the old enum
# types to standard varchars in the bugs table.
$dbh->bz_alter_column('bugs', 'bug_status',
{TYPE => 'varchar(64)', NOTNULL => 1});
# 2005-03-27 initialqacontact should be NULL instead of 0, bug 287483
if ($dbh->bz_column_info('components', 'initialqacontact')->{NOTNULL}) {
- $dbh->bz_alter_column('components', 'initialqacontact',
+ $dbh->bz_alter_column('components', 'initialqacontact',
{TYPE => 'INT3'});
}
$dbh->do("UPDATE components SET initialqacontact = NULL " .
{TYPE => 'TINYTEXT', NOTNULL => 1, DEFAULT => "''"});
_clean_control_characters_from_short_desc();
-
+
# 2005-12-07 altlst@sonic.net -- Bug 225221
$dbh->bz_add_column('longdescs', 'comment_id',
{TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
if ($dbh->bz_column_info('products', 'disallownew')){
$dbh->bz_alter_column('products', 'disallownew',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
-
+
if ($dbh->bz_column_info('products', 'votesperuser')) {
- $dbh->bz_alter_column('products', 'votesperuser',
+ $dbh->bz_alter_column('products', 'votesperuser',
{TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
$dbh->bz_alter_column('products', 'votestoconfirm',
{TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
$dbh->bz_add_column('setting', 'subclass', {TYPE => 'varchar(32)'});
- $dbh->bz_alter_column('longdescs', 'thetext',
+ $dbh->bz_alter_column('longdescs', 'thetext',
{TYPE => 'LONGTEXT', NOTNULL => 1}, '');
# 2006-10-20 LpSolit@gmail.com - Bug 189627
{TYPE => 'INT2', NOTNULL => 1, DEFAULT => '0'});
$dbh->bz_add_column('longdescs', 'extra_data', {TYPE => 'varchar(255)'});
- $dbh->bz_add_column('versions', 'id',
+ $dbh->bz_add_column('versions', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
$dbh->bz_add_column('milestones', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
# 2007-08-21 wurblzap@gmail.com - Bug 365378
_make_lang_setting_dynamic();
-
+
# 2007-11-29 xiaoou.wu@oracle.com - Bug 153129
_change_text_types();
# 2009-01-16 oreomike@gmail.com - Bug 302420
$dbh->bz_add_column('whine_events', 'mailifnobugs',
{ TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
-
+
_convert_disallownew_to_isactive();
- $dbh->bz_alter_column('bugs_activity', 'added',
+ $dbh->bz_alter_column('bugs_activity', 'added',
{ TYPE => 'varchar(255)' });
$dbh->bz_add_index('bugs_activity', 'bugs_activity_added_idx', ['added']);
# 2010-04-07 LpSolit@gmail.com - Bug 69621
$dbh->bz_drop_column('bugs', 'keywords');
-
+
# 2010-05-07 ewong@pw-wspx.org - Bug 463945
$dbh->bz_alter_column('group_control_map', 'membercontrol',
{TYPE => 'INT1', NOTNULL => 1, DEFAULT => CONTROLMAPNA});
# Add NOT NULL to some columns that need it, and DEFAULT to
# attachments.ispatch.
- $dbh->bz_alter_column('attachments', 'ispatch',
+ $dbh->bz_alter_column('attachments', 'ispatch',
{ TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
$dbh->bz_alter_column('keyworddefs', 'description',
{ TYPE => 'MEDIUMTEXT', NOTNULL => 1 }, '');
}
# 2012-06-06 dkl@mozilla.com - Bug 762288
- $dbh->bz_alter_column('bugs_activity', 'removed',
+ $dbh->bz_alter_column('bugs_activity', 'removed',
{ TYPE => 'varchar(255)' });
$dbh->bz_add_index('bugs_activity', 'bugs_activity_removed_idx', ['removed']);
# 2012-06-13 dkl@mozilla.com - Bug 764457
- $dbh->bz_add_column('bugs_activity', 'id',
+ $dbh->bz_add_column('bugs_activity', 'id',
{TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
# 2012-06-13 dkl@mozilla.com - Bug 764466
- $dbh->bz_add_column('profiles_activity', 'id',
+ $dbh->bz_add_column('profiles_activity', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
# 2012-07-24 dkl@mozilla.com - Bug 776972
Bugzilla::Hook::process('install_update_db');
- # We do this here because otherwise the foreign key from
+ # We do this here because otherwise the foreign key from
# products.classification_id to classifications.id will fail
# (because products.classification_id defaults to "1", so on upgraded
# installations it's already been set before the first Classification
sub _add_bug_vote_cache {
my $dbh = Bugzilla->dbh;
- # 1999-10-11 Restructured voting database to add a cached value in each
- # bug recording how many total votes that bug has. While I'm at it,
- # I removed the unused "area" field from the bugs database. It is
- # distressing to realize that the bugs table has reached the maximum
- # number of indices allowed by MySQL (16), which may make future
+ # 1999-10-11 Restructured voting database to add a cached value in each
+ # bug recording how many total votes that bug has. While I'm at it,
+ # I removed the unused "area" field from the bugs database. It is
+ # distressing to realize that the bugs table has reached the maximum
+ # number of indices allowed by MySQL (16), which may make future
# enhancements awkward.
- # (P.S. All is not lost; it appears that the latest betas of MySQL
+ # (P.S. All is not lost; it appears that the latest betas of MySQL
# support a new table format which will allow 32 indices.)
if ($dbh->bz_column_info('bugs', 'area')) {
$buffer = trim($buffer);
return if !$buffer;
$dbh->do("INSERT INTO longdescs (bug_id, who, bug_when, thetext)
- VALUES (?,?,?,?)", undef, $id, $who,
+ VALUES (?,?,?,?)", undef, $id, $who,
time2str("%Y/%m/%d %H:%M:%S", $when), $buffer);
}
sub _populate_longdescs {
my $dbh = Bugzilla->dbh;
- # 2000-01-20 Added a new "longdescs" table, which is supposed to have
- # all the long descriptions in it, replacing the old long_desc field
- # in the bugs table. The below hideous code populates this new table
+ # 2000-01-20 Added a new "longdescs" table, which is supposed to have
+ # all the long descriptions in it, replacing the old long_desc field
+ # in the bugs table. The below hideous code populates this new table
# with things from the old field, with ugly parsing and heuristics.
if ($dbh->bz_column_info('bugs', 'long_desc')) {
long_desc FROM bugs ORDER BY bug_id");
$sth->execute();
my $count = 0;
- while (my ($id, $createtime, $reporterid, $desc) =
- $sth->fetchrow_array())
+ while (my ($id, $createtime, $reporterid, $desc) =
+ $sth->fetchrow_array())
{
$count++;
indicate_progress({ total => $total, current => $count });
my $buffer = "";
foreach my $line (split(/\n/, $desc)) {
$line =~ s/\s+$//g; # Trim trailing whitespace.
- if ($line =~ /^------- Additional Comments From ([^\s]+)\s+(\d.+\d)\s+-------$/)
+ if ($line =~ /^------- Additional Comments From ([^\s]+)\s+(\d.+\d)\s+-------$/)
{
my $name = $1;
my $date = str2time($2);
# Oy, what a hack. The creation time is accurate to the
# second. But the long text only contains things accurate
- # to the And so, if someone makes a comment within a
+ # to the And so, if someone makes a comment within a
# minute of the original bug creation, then the comment can
# come *before* the bug creation. So, we add 59 seconds to
- # the time of all comments, so that they are always
+ # the time of all comments, so that they are always
# considered to have happened at the *end* of the given
# minute, not the beginning.
$date += 59;
# This username doesn't exist. Maybe someone
# renamed him or something. Invent a new profile
# entry disabled, just to represent him.
- $dbh->do("INSERT INTO profiles (login_name,
- cryptpassword, disabledtext)
+ $dbh->do("INSERT INTO profiles (login_name,
+ cryptpassword, disabledtext)
VALUES (?,?,?)", undef, $name, '*',
"Account created only to maintain"
. " database integrity");
my $ids = $dbh->selectall_arrayref(
'SELECT DISTINCT fielddefs.id, bugs_activity.field
- FROM bugs_activity LEFT JOIN fielddefs
+ FROM bugs_activity LEFT JOIN fielddefs
ON bugs_activity.field = fielddefs.name', {Slice=>{}});
foreach my $item (@$ids) {
my $sth = $dbh->prepare("SELECT program, value, initialqacontact
FROM components");
$sth->execute();
- while (my ($program, $value, $initialqacontact) =
- $sth->fetchrow_array())
+ while (my ($program, $value, $initialqacontact) =
+ $sth->fetchrow_array())
{
my ($id) = $dbh->selectrow_array(
"SELECT userid FROM profiles WHERE login_name = ?",
}
# Populate the milestone table with all existing values in the database
- my $sth = $dbh->prepare("SELECT DISTINCT target_milestone, product
+ my $sth = $dbh->prepare("SELECT DISTINCT target_milestone, product
FROM bugs");
$sth->execute();
WHERE value = ? AND product = ?", undef, $value, $product);
if (!$ms_exists) {
- $dbh->do("INSERT INTO milestones(value, product, sortkey)
+ $dbh->do("INSERT INTO milestones(value, product, sortkey)
VALUES (?,?,?)", undef, $value, $product, $sortkey);
}
}
while (my ($product, $default_ms) = $sth->fetchrow_array()) {
my $exists = $dbh->selectrow_array(
"SELECT value FROM milestones
- WHERE value = ? AND product = ?",
+ WHERE value = ? AND product = ?",
undef, $default_ms, $product);
if (!$exists) {
$dbh->do("INSERT INTO milestones(value, product) " .
my $dbh = Bugzilla->dbh;
# 2000-07-15 Added duplicates table so Bugzilla tracks duplicates in a
# better way than it used to. This code searches the comments to populate
- # the table initially. It's executed if the table is empty; if it's
- # empty because there are no dupes (as opposed to having just created
+ # the table initially. It's executed if the table is empty; if it's
+ # empty because there are no dupes (as opposed to having just created
# the table) it won't have any effect anyway, so it doesn't matter.
my ($dups_exist) = $dbh->selectrow_array(
"SELECT DISTINCT 1 FROM duplicates");
my $sth = $dbh->prepare(
"SELECT longdescs.bug_id, thetext
- FROM longdescs LEFT JOIN bugs
+ FROM longdescs LEFT JOIN bugs
ON longdescs.bug_id = bugs.bug_id
- WHERE (" . $dbh->sql_regexp("thetext",
+ WHERE (" . $dbh->sql_regexp("thetext",
"'[.*.]{3} This bug has been marked as a duplicate"
- . " of [[:digit:]]+ [.*.]{3}'")
+ . " of [[:digit:]]+ [.*.]{3}'")
. ")
AND resolution = 'DUPLICATE'
ORDER BY longdescs.bug_when");
$sth->execute();
my (%dupes, $key);
- # Because of the way hashes work, this loop removes all but the
+ # Because of the way hashes work, this loop removes all but the
# last dupe resolution found for a given bug.
while (my ($dupe, $dupe_of) = $sth->fetchrow_array()) {
$dupes{$dupe} = $dupe_of;
$sth->execute;
my $i = 0;
while (my ($bug_id, $who, $bug_when, $fieldid, $oldvalue, $newvalue)
- = $sth->fetchrow_array())
+ = $sth->fetchrow_array())
{
$i++;
indicate_progress({ total => $total, current => $i, every => 10 });
}
$added = $dbh->quote($added);
$removed = $dbh->quote($removed);
- $dbh->do("UPDATE bugs_activity
+ $dbh->do("UPDATE bugs_activity
SET removed = $removed, added = $added
WHERE bug_id = $bug_id AND who = $who
- AND bug_when = '$bug_when'
+ AND bug_when = '$bug_when'
AND fieldid = $fieldid");
}
print "\n";
$dbh->bz_add_column("bugs", "product_id",
{TYPE => 'INT2', NOTNULL => 1}, 0);
- # The attachstatusdefs table was added in version 2.15, but
- # removed again in early 2.17. If it exists now, we still need
+ # The attachstatusdefs table was added in version 2.15, but
+ # removed again in early 2.17. If it exists now, we still need
# to perform this change with product_id because the code later on
# which converts the attachment statuses to flags depends on it.
# But we need to avoid this if the user is upgrading from 2.14
my %components;
$sth = $dbh->prepare("SELECT id, value, product_id FROM components");
$sth->execute;
- while (my ($component_id, $component, $product_id)
- = $sth->fetchrow_array())
+ while (my ($component_id, $component, $product_id)
+ = $sth->fetchrow_array())
{
if (exists $components{$component}) {
if (exists $components{$component}{$product_id}) {
#
# This requires:
# 1) define groups ids in group table
- # 2) populate user_group_map with grants from old groupsets
+ # 2) populate user_group_map with grants from old groupsets
# and blessgroupsets
# 3) populate bug_group_map with data converted from old bug groupsets
# 4) convert activity logs to use group names instead of numbers
VALUES ($bug_id, $gid)");
}
}
- # Replace old activity log groupset records with lists of names
+ # Replace old activity log groupset records with lists of names
# of groups.
$sth = $dbh->prepare("SELECT id FROM fielddefs
WHERE name = " . $dbh->quote('bug_group'));
$sth = $dbh->prepare("SELECT bug_id, bug_when, who, added, removed
FROM bugs_activity WHERE fieldid = $gsid");
$sth->execute();
- while (my ($bug_id, $bug_when, $who, $added, $removed) =
- $sth->fetchrow_array)
+ while (my ($bug_id, $bug_when, $who, $added, $removed) =
+ $sth->fetchrow_array)
{
$added ||= 0;
$removed ||= 0;
while (my ($n) = $sth2->fetchrow_array) {
push @logrem, $n;
}
- # Get list of group bits added that correspond to
+ # Get list of group bits added that correspond to
# missing groups.
- $sth2 = $dbh->prepare("SELECT ($added & ~BIT_OR(bit))
+ $sth2 = $dbh->prepare("SELECT ($added & ~BIT_OR(bit))
FROM groups");
$sth2->execute();
my ($miss) = $sth2->fetchrow_array;
print "\nWARNING - GROUPSET ACTIVITY ON BUG $bug_id",
" CONTAINS DELETED GROUPS\n";
}
- # Get list of group bits deleted that correspond to
+ # Get list of group bits deleted that correspond to
# missing groups.
- $sth2 = $dbh->prepare("SELECT ($removed & ~BIT_OR(bit))
+ $sth2 = $dbh->prepare("SELECT ($removed & ~BIT_OR(bit))
FROM groups");
$sth2->execute();
($miss) = $sth2->fetchrow_array;
"FROM profiles_activity " .
"WHERE fieldid = $gsid");
$sth->execute();
- while (my ($uid, $uwhen, $uwho, $added, $removed) =
- $sth->fetchrow_array)
+ while (my ($uid, $uwhen, $uwho, $added, $removed) =
+ $sth->fetchrow_array)
{
$added ||= 0;
$removed ||= 0;
"FROM profiles " .
"WHERE (groupset | 65536) = 9223372036854775807");
$sth->execute();
- while ( my ($userid, $iscomplete, $login_name)
- = $sth->fetchrow_array() )
+ while ( my ($userid, $iscomplete, $login_name)
+ = $sth->fetchrow_array() )
{
# existing administrators are made members of group "admin"
print "\nWARNING - $login_name IS AN ADMIN IN SPITE OF BUG",
foreach my $admin_id (@admins) {
$dbh->do("INSERT INTO user_group_map
(user_id, group_id, isbless, grant_type)
- VALUES (?, ?, ?, ?)",
+ VALUES (?, ?, ?, ?)",
undef, $admin_id, $admin_gid, $_, GRANT_DIRECT)
foreach (0, 1);
}
$dbh->bz_drop_column('profiles','blessgroupset');
$dbh->bz_drop_column('bugs','groupset');
$dbh->bz_drop_column('groups','bit');
- $dbh->do("DELETE FROM fielddefs WHERE name = "
+ $dbh->do("DELETE FROM fielddefs WHERE name = "
. $dbh->quote('groupset'));
}
}
# September 2002 myk@mozilla.org bug 98801
# Convert the attachment statuses tables into flags tables.
- if ($dbh->bz_table_info("attachstatuses")
- && $dbh->bz_table_info("attachstatusdefs"))
+ if ($dbh->bz_table_info("attachstatuses")
+ && $dbh->bz_table_info("attachstatusdefs"))
{
print "Converting attachment statuses to flags...\n";
# Convert attachment status definitions to flag types. If more than one
# status has the same name and description, it is merged into a single
- # status with multiple inclusion records.
+ # status with multiple inclusion records.
my $sth = $dbh->prepare(
- "SELECT id, name, description, sortkey, product_id
+ "SELECT id, name, description, sortkey, product_id
FROM attachstatusdefs");
# status definition IDs indexed by name/description
my $def_id_map = {};
$sth->execute();
- while (my ($id, $name, $desc, $sortkey, $prod_id) =
- $sth->fetchrow_array())
+ while (my ($id, $name, $desc, $sortkey, $prod_id) =
+ $sth->fetchrow_array())
{
my $key = $name . $desc;
if (!$def_ids->{$key}) {
$def_ids->{$key} = $id;
my $quoted_name = $dbh->quote($name);
my $quoted_desc = $dbh->quote($desc);
- $dbh->do("INSERT INTO flagtypes (id, name, description,
- sortkey, target_type)
- VALUES ($id, $quoted_name, $quoted_desc,
+ $dbh->do("INSERT INTO flagtypes (id, name, description,
+ sortkey, target_type)
+ VALUES ($id, $quoted_name, $quoted_desc,
$sortkey,'a')");
}
$def_id_map->{$id} = $def_ids->{$key};
}
# Note: even though we've converted status definitions, we still
- # can't drop the table because we need it to convert the statuses
+ # can't drop the table because we need it to convert the statuses
# themselves.
- # Convert attachment statuses to flags. To do this we select
- # the statuses from the status table and then, for each one,
- # figure out who set it and when they set it from the bugs
+ # Convert attachment statuses to flags. To do this we select
+ # the statuses from the status table and then, for each one,
+ # figure out who set it and when they set it from the bugs
# activity table.
my $id = 0;
$sth = $dbh->prepare(
ORDER BY bug_when DESC");
$sth->execute();
- while (my ($attach_id, $def_id, $status, $bug_id) =
- $sth->fetchrow_array())
+ while (my ($attach_id, $def_id, $status, $bug_id) =
+ $sth->fetchrow_array())
{
++$id;
$when = $when ? $dbh->quote($when) : "NOW()";
- $dbh->do("INSERT INTO flags (id, type_id, status, bug_id,
- attach_id, creation_date, modification_date,
+ $dbh->do("INSERT INTO flags (id, type_id, status, bug_id,
+ attach_id, creation_date, modification_date,
requestee_id, setter_id)
VALUES ($id, $def_id_map->{$def_id}, '+', $bug_id,
$attach_id, $when, $when, NULL, $who)");
$dbh->bz_drop_table("attachstatuses");
$dbh->bz_drop_table("attachstatusdefs");
- # Convert activity records for attachment statuses into records
+ # Convert activity records for attachment statuses into records
# for flags.
- $sth = $dbh->prepare("SELECT attach_id, who, bug_when, added,
- removed
- FROM bugs_activity
+ $sth = $dbh->prepare("SELECT attach_id, who, bug_when, added,
+ removed
+ FROM bugs_activity
WHERE fieldid = $old_field_id");
$sth->execute();
while (my ($attach_id, $who, $when, $old_added, $old_removed) =
$sth->execute();
my %flagtypes;
- my @badflagnames;
+ my @badflagnames;
# find broken flagtype names, and populate a hash table
# to check for collisions.
while (my ($name, $id) = $sth->fetchrow_array()) {
# Initially populate group_control_map.
# First, get all the existing products and their groups.
my $sth = $dbh->prepare("SELECT groups.id, products.id, groups.name,
- products.name
+ products.name
FROM groups, products
WHERE isbuggroup != 0");
$sth->execute();
while (my ($groupid, $productid, $groupname, $productname)
- = $sth->fetchrow_array())
+ = $sth->fetchrow_array())
{
if ($groupname eq $productname) {
# Product and group have same name.
($groupid, $productid, CONTROLMAPDEFAULT, CONTROLMAPNA));
} else {
# See if this group is a product group at all.
- my $sth2 = $dbh->prepare("SELECT id FROM products
+ my $sth2 = $dbh->prepare("SELECT id FROM products
WHERE name = " .$dbh->quote($groupname));
$sth2->execute();
my ($id) = $sth2->fetchrow_array();
# The query for statuses is different to that for resolutions.
$queries{$_} = ($query_prod . "bug_status=$_") foreach (@statuses);
- $queries{$_} = ($query_prod . "resolution=$_")
+ $queries{$_} = ($query_prod . "resolution=$_")
foreach (@resolutions);
foreach my $field (@fields) {
$product_file =~ s/\//-/gs;
$product_file = "$datadir/mining/$product_file";
- # There are many reasons that this might fail (e.g. no stats
+ # There are many reasons that this might fail (e.g. no stats
# for this product), so we don't worry if it does.
my $in = new IO::File($product_file) or next;
$in->close;
- my $total_items = (scalar(@fields) + 1)
+ my $total_items = (scalar(@fields) + 1)
* scalar(keys %{ $data{'NEW'} });
my $count = 0;
foreach my $field (@fields, $open_name) {
# Insert values into series_data: series_id, date, value
my %fielddata = %{$data{$field}};
foreach my $date (keys %fielddata) {
- # We need to delete in case the text file had duplicate
+ # We need to delete in case the text file had duplicate
# entries in it.
$deletesth->execute($seriesids{$field}, $date);
# We prepared this above
$seriesdatasth->execute($seriesids{$field},
$date, $fielddata{$date} || 0);
- indicate_progress({ total => $total_items,
+ indicate_progress({ total => $total_items,
current => ++$count, every => 100 });
}
}
$dbh->bz_drop_index('group_group_map', 'group_group_map_member_id_idx');
$dbh->bz_drop_column("group_group_map", "isbless");
$dbh->bz_add_index('group_group_map', 'group_group_map_member_id_idx',
- {TYPE => 'UNIQUE',
+ {TYPE => 'UNIQUE',
FIELDS => [qw(member_id grantor_id grant_type)]});
}
}
foreach my $relationship (keys %relationships) {
foreach my $event (keys %events) {
my $key = "email$relationship$event";
- if (!exists($emailflags{$key})
- || $emailflags{$key} eq 'on')
+ if (!exists($emailflags{$key})
+ || $emailflags{$key} eq 'on')
{
$sth2->execute($relationships{$relationship},
$events{$event});
}
}
}
- # Note that in the old system, the value of "excludeself" is
- # assumed to be off if the preference does not exist in the
- # user's list, unlike other preferences whose value is
+ # Note that in the old system, the value of "excludeself" is
+ # assumed to be off if the preference does not exist in the
+ # user's list, unlike other preferences whose value is
# assumed to be on if they do not exist.
#
# This preference has changed from global to per-relationship.
if ($dbh->bz_index_info('attachments', 'attachments_submitter_id_idx')
&& (scalar(@{$dbh->bz_index_info('attachments',
'attachments_submitter_id_idx'
- )->{FIELDS}}) < 2))
+ )->{FIELDS}}) < 2))
{
$dbh->bz_drop_index('attachments', 'attachments_submitter_id_idx');
}
$dbh->prepare("SELECT series_id FROM series WHERE query IN (?, ?)");
# Statement to find the series which has collected the most data.
my $sth_data_collected =
- $dbh->prepare('SELECT count(*) FROM series_data
+ $dbh->prepare('SELECT count(*) FROM series_data
WHERE series_id = ?');
# Statement to select a broken non-open bugs count data entry.
my $sth_select_broken_nonopen_data =
my $sth_fix_broken_nonopen_data =
$dbh->prepare('UPDATE series_data SET series_value = ?' .
' WHERE series_id = ? AND series_date = ?');
- # Statement to delete an unfixable broken non-open bugs count data
+ # Statement to delete an unfixable broken non-open bugs count data
# entry.
my $sth_delete_broken_nonopen_data =
$dbh->prepare('DELETE FROM series_data' .
if ($nonopen_bugs_query =~ /^$broken_series_indicator(.*)$/) {
my $prodcomp = $1;
- # If there is more than one series for the corresponding
+ # If there is more than one series for the corresponding
# open-bugs series, we pick the one with the most data,
# which should be the one which was generated on creation.
# It's a pity we can't do subselects.
print " $broken_series_id...";
$sth_select_broken_nonopen_data->execute($broken_series_id);
while (my $rowref =
- $sth_select_broken_nonopen_data->fetchrow_arrayref)
+ $sth_select_broken_nonopen_data->fetchrow_arrayref)
{
my ($date, $broken_value) = @$rowref;
my ($openbugs_value) =
}
}
- # Fix the broken query so that it collects correct data
+ # Fix the broken query so that it collects correct data
# in the future.
$nonopen_bugs_query =~
s/^$broken_series_indicator/field0-0-0=resolution&type0-0-0=regexp&value0-0-0=./;
- $sth_repair->execute($nonopen_bugs_query,
+ $sth_repair->execute($nonopen_bugs_query,
$broken_series_id);
}
else {
print <<EOT;
-WARNING - Series $broken_series_id was meant to collect non-open bug
+WARNING - Series $broken_series_id was meant to collect non-open bug
counts, but it has counted all bugs instead. It cannot be repaired
automatically because no series that collected open bug counts was found.
-You'll probably want to delete or repair collected data for
+You'll probably want to delete or repair collected data for
series $broken_series_id manually
Continuing repairs...
} # if (@$broken_nonopen_series)
}
-# This needs to happen at two times: when we upgrade from 2.16 (thus creating
+# This needs to happen at two times: when we upgrade from 2.16 (thus creating
# user_group_map), and when we kill derived gruops in the DB.
sub _rederive_regex_groups {
my $dbh = Bugzilla->dbh;
return if !$regex_groups_exist;
my $regex_derivations = $dbh->selectrow_array(
- 'SELECT 1 FROM user_group_map WHERE grant_type = ' . GRANT_REGEXP
+ 'SELECT 1 FROM user_group_map WHERE grant_type = ' . GRANT_REGEXP
. ' ' . $dbh->sql_limit(1));
return if $regex_derivations;
# Re-evaluate all regexps, to keep them up-to-date.
my $sth = $dbh->prepare(
- "SELECT profiles.userid, profiles.login_name, groups.id,
+ "SELECT profiles.userid, profiles.login_name, groups.id,
groups.userregexp, user_group_map.group_id
FROM (profiles CROSS JOIN groups)
LEFT JOIN user_group_map
my $sth_del = $dbh->prepare(
"DELETE FROM user_group_map
- WHERE user_id = ? AND group_id = ? AND isbless = 0
+ WHERE user_id = ? AND group_id = ? AND isbless = 0
AND grant_type = " . GRANT_REGEXP);
$sth->execute(GRANT_REGEXP);
- while (my ($uid, $login, $gid, $rexp, $present) =
- $sth->fetchrow_array())
+ while (my ($uid, $login, $gid, $rexp, $present) =
+ $sth->fetchrow_array())
{
if ($login =~ m/$rexp/i) {
$sth_add->execute($uid, $gid) unless $present;
}
sub _change_text_types {
- my $dbh = Bugzilla->dbh;
- return if
+ my $dbh = Bugzilla->dbh;
+ return if
$dbh->bz_column_info('namedqueries', 'query')->{TYPE} eq 'LONGTEXT';
_check_content_length('attachments', 'mimetype', 255, 'attach_id');
_check_content_length('fielddefs', 'description', 255, 'id');
$dbh->bz_alter_column('namedqueries', 'query',
{ TYPE => 'LONGTEXT', NOTNULL => 1 });
-}
+}
sub _check_content_length {
my ($table_name, $field_name, $max_length, $id_field) = @_;
my $dbh = Bugzilla->dbh;
my %contents = @{ $dbh->selectcol_arrayref(
- "SELECT $id_field, $field_name FROM $table_name
+ "SELECT $id_field, $field_name FROM $table_name
WHERE CHAR_LENGTH($field_name) > ?", {Columns=>[1,2]}, $max_length) };
if (scalar keys %contents) {
my $string = $contents{$id};
# Don't dump the whole string--it could be 16MB.
if (length($string) > 80) {
- $string = substr($string, 0, 30) . "..."
+ $string = substr($string, 0, 30) . "..."
. substr($string, -30) . "\n";
}
$error .= "$id: $string\n";
my $dbh = Bugzilla->dbh;
my $names = $dbh->selectcol_arrayref(
- 'SELECT name
- FROM fielddefs
+ 'SELECT name
+ FROM fielddefs
WHERE type = ' . FIELD_TYPE_MULTI_SELECT);
foreach my $name (@$names) {
- $dbh->bz_add_fk("bug_$name", "bug_id",
+ $dbh->bz_add_fk("bug_$name", "bug_id",
{TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'});
-
+
$dbh->bz_add_fk("bug_$name", "value",
{TABLE => $name, COLUMN => 'value', DELETE => 'RESTRICT'});
}
$command = "REPLACE";
}
else {
- $dbh->do("DELETE FROM bugs_fulltext WHERE "
+ $dbh->do("DELETE FROM bugs_fulltext WHERE "
. $dbh->sql_in('bug_id', $bug_ids));
}
}
# As recommended by Monty Widenius for GNOME's upgrade.
# mkanat and justdave concur it'll be helpful for bmo, too.
$dbh->do('SET SESSION myisam_sort_buffer_size = 3221225472');
-
+
my $newline = $dbh->quote("\n");
$dbh->do(
- qq{$command INTO bugs_fulltext (bug_id, short_desc, comments,
+ qq{$command INTO bugs_fulltext (bug_id, short_desc, comments,
comments_noprivate)
SELECT bugs.bug_id, bugs.short_desc, }
. $dbh->sql_group_concat('longdescs.thetext', $newline, 0)
. ', ' . $dbh->sql_group_concat('nopriv.thetext', $newline, 0) .
- qq{ FROM bugs
+ qq{ FROM bugs
LEFT JOIN longdescs
ON bugs.bug_id = longdescs.bug_id
LEFT JOIN longdescs AS nopriv
ON longdescs.comment_id = nopriv.comment_id
- AND nopriv.isprivate = 0
+ AND nopriv.isprivate = 0
$where }
. $dbh->sql_group_by('bugs.bug_id', 'bugs.short_desc'));
}
sub _add_visiblity_value_to_value_tables {
my $dbh = Bugzilla->dbh;
- my @standard_fields =
+ my @standard_fields =
qw(bug_status resolution priority bug_severity op_sys rep_platform);
my $custom_fields = $dbh->selectcol_arrayref(
'SELECT name FROM fielddefs WHERE custom = 1 AND type IN(?,?)',
sub _convert_disallownew_to_isactive {
my $dbh = Bugzilla->dbh;
if ($dbh->bz_column_info('products', 'disallownew')){
- $dbh->bz_add_column('products', 'isactive',
+ $dbh->bz_add_column('products', 'isactive',
{ TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
-
+
# isactive is the boolean reverse of disallownew.
$dbh->do('UPDATE products SET isactive = 0 WHERE disallownew = 1');
$dbh->do('UPDATE products SET isactive = 1 WHERE disallownew = 0');
-
+
$dbh->bz_drop_column('products','disallownew');
}
}
sub _set_attachment_comment_type {
my ($type, $string) = @_;
my $dbh = Bugzilla->dbh;
- # We check if there are any comments of this type already, first,
+ # We check if there are any comments of this type already, first,
# because this is faster than a full LIKE search on the comments,
# and currently this will run every time we run checksetup.
my $test = $dbh->selectrow_array(
return [] if $test;
my %comments = @{ $dbh->selectcol_arrayref(
"SELECT comment_id, thetext FROM longdescs
- WHERE thetext LIKE '$string%'",
+ WHERE thetext LIKE '$string%'",
{Columns=>[1,2]}) };
my @comment_ids = keys %comments;
return [] if !scalar @comment_ids;
}
$text = join("\n", @lines);
$sth->execute($text, $type, $attachment_id, $id);
- indicate_progress({ total => $total, current => $count,
+ indicate_progress({ total => $total, current => $count,
every => 25 });
}
return \@comment_ids;
$dbh->bz_add_column('products', 'allows_unconfirmed',
{ TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE' });
if ($dbh->bz_column_info('products', 'votestoconfirm')) {
- $dbh->do('UPDATE products SET allows_unconfirmed = 1
+ $dbh->do('UPDATE products SET allows_unconfirmed = 1
WHERE votestoconfirm > 0');
}
}
# If we add the isactive column all values should start off as active
if (!$dbh->bz_column_info('components', 'isactive')) {
- $dbh->bz_add_column('components', 'isactive',
+ $dbh->bz_add_column('components', 'isactive',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
}
-
+
if (!$dbh->bz_column_info('versions', 'isactive')) {
- $dbh->bz_add_column('versions', 'isactive',
+ $dbh->bz_add_column('versions', 'isactive',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
}
if (!$dbh->bz_column_info('milestones', 'isactive')) {
- $dbh->bz_add_column('milestones', 'isactive',
+ $dbh->bz_add_column('milestones', 'isactive',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
}
}
my $update_sth =
$dbh->prepare("UPDATE bug_see_also SET class = ? WHERE id = ?");
-
+
$dbh->bz_start_transaction();
foreach my $see_also (@$result) {
my ($id, $value) = @$see_also;
if (!$dbh->bz_column_info('profiles', 'is_enabled')) {
$dbh->bz_add_column("profiles", 'is_enabled',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
- $dbh->do("UPDATE profiles SET is_enabled = 0
+ $dbh->do("UPDATE profiles SET is_enabled = 0
WHERE disabledtext != ''");
}
}
sub _fix_notnull_defaults {
my $dbh = Bugzilla->dbh;
- $dbh->bz_alter_column('bugs', 'bug_file_loc',
- {TYPE => 'MEDIUMTEXT', NOTNULL => 1,
+ $dbh->bz_alter_column('bugs', 'bug_file_loc',
+ {TYPE => 'MEDIUMTEXT', NOTNULL => 1,
DEFAULT => "''"}, '');
- my $custom_fields = Bugzilla::Field->match({
- custom => 1, type => [ FIELD_TYPE_FREETEXT, FIELD_TYPE_TEXTAREA ]
+ my $custom_fields = Bugzilla::Field->match({
+ custom => 1, type => [ FIELD_TYPE_FREETEXT, FIELD_TYPE_TEXTAREA ]
});
foreach my $field (@$custom_fields) {
$dbh->bz_drop_index('dependencies', 'dependencies_blocked_idx');
$dbh->bz_add_index('dependencies', 'dependencies_blocked_idx',
{ FIELDS => [qw(blocked dependson)], TYPE => 'UNIQUE' });
- }
+ }
}
sub _fix_flagclusions_indexes {
{ Slice => {} });
print "Removing duplicated entries from the '$table' table...\n" if @$dupes;
foreach my $dupe (@$dupes) {
- $dbh->do("DELETE FROM $table
+ $dbh->do("DELETE FROM $table
WHERE type_id = ? AND product_id = ? AND component_id = ?",
undef, $dupe->{type_id}, $dupe->{product_id}, $dupe->{component_id});
$dbh->do("INSERT INTO $table (type_id, product_id, component_id) VALUES (?, ?, ?)",
=head1 DESCRIPTION
-This module is used primarily by L<checksetup.pl> to modify the
+This module is used primarily by L<checksetup.pl> to modify the
database during upgrades.
=head1 SUBROUTINES
=item C<update_table_definitions()>
Description: This is the primary code that updates table definitions
- during upgrades. If you modify the schema in some
- way, you should add code to the end of this function to
+ during upgrades. If you modify the schema in some
+ way, you should add code to the end of this function to
make sure that your modifications happen over all installations.
Params: none
# scripts need to overwrite files created by other users.
sub DIR_CGI_OVERWRITE { _group() ? 0770 : 0777 };
-# This can be combined (using "|") with other permissions for
+# This can be combined (using "|") with other permissions for
# directories that, in addition to their normal permissions (such
# as DIR_CGI_WRITE) also have content served directly from them
# (or their subdirectories) to the user, via the webserver.
# This looks like a constant because it effectively is, but
# it has to call other subroutines and read the current filesystem,
# so it's defined as a sub. This is not exported, so it doesn't have
-# a perldoc. However, look at the various hashes defined inside this
+# a perldoc. However, look at the various hashes defined inside this
# function to understand what it returns. (There are comments throughout.)
#
# The rationale for the file permissions is that there is a group the
docs => DIR_WS_SERVE,
);
- # This sets the permissions for each item inside each of these
- # directories, including the directory itself.
- # 'CVS' directories are special, though, and are never readable by
+ # This sets the permissions for each item inside each of these
+ # directories, including the directory itself.
+ # 'CVS' directories are special, though, and are never readable by
# the webserver.
my %recurse_dirs = (
# Writeable directories
# The name of each file, pointing at its default permissions and
# default contents.
my %create_files = (
- "$datadir/extensions/additional" => { perms => CGI_READ,
+ "$datadir/extensions/additional" => { perms => CGI_READ,
contents => '' },
# We create this file so that it always has the right owner
# and permissions. Otherwise, the webserver creates it as
my $graphsdir = bz_locations->{'graphsdir'};
my $assetsdir = bz_locations->{'assetsdir'};
# If the graphs/ directory doesn't exist, we're upgrading from
- # a version old enough that we need to update the $datadir/mining
+ # a version old enough that we need to update the $datadir/mining
# format.
if (-d "$datadir/mining" && !-d $graphsdir) {
_update_old_charts($datadir);
# If you ran a REALLY old version of Bugzilla, your chart files are in the
# wrong format. This code is a little messy, because it's very old, and
-# when moving it into this module, I couldn't test it so I left it almost
+# when moving it into this module, I couldn't test it so I left it almost
# completely alone.
sub _update_old_charts {
my ($datadir) = @_;
" You may want to check your data files.\n";
}
- print OUT join('|',
+ print OUT join('|',
map { defined ($data{$_}) ? ($data{$_}) : "" } @out_fields),
"\n";
}
close(IN);
close(OUT);
- }
+ }
}
sub fix_dir_permissions {
foreach my $filename (glob $file) {
# Don't touch directories.
next if -d $filename || !-e $filename;
- _fix_perms($filename, $owner_id, $group_id,
+ _fix_perms($filename, $owner_id, $group_id,
$files{$file}->{perms});
}
}
find({ no_chdir => 1, wanted => sub {
my $name = $File::Find::name;
if ($File::Find::dir =~ /\/CVS/ || $_ eq '.cvsignore'
- || (-d $name && $_ =~ /CVS$/))
+ || (-d $name && $_ =~ /CVS$/))
{
my $perms = 0600;
if (-d $name) {
# The webserver should never try to chown files.
if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
chown $owner, $group, $name
- or warn install_string('chown_failed', { path => $name,
+ or warn install_string('chown_failed', { path => $name,
error => $! }) . "\n";
}
chmod $perms, $name
- or warn install_string('chmod_failed', { path => $name,
+ or warn install_string('chmod_failed', { path => $name,
error => $! }) . "\n";
}
# the webservergroup.
elsif (!ON_WINDOWS && $group) {
$group_id = getgrnam($group);
- ThrowCodeError('invalid_webservergroup', { group => $group })
+ ThrowCodeError('invalid_webservergroup', { group => $group })
unless defined $group_id;
# If on unix, see if we need to print a warning about a webservergroup
=head1 DESCRIPTION
-This module is used primarily by L<checksetup.pl> to modify the
+This module is used primarily by L<checksetup.pl> to modify the
filesystem during installation, including creating the data/ directory.
=head1 SUBROUTINES
},
{
name => 'db_host',
- default => 'localhost',
+ default => 'localhost',
},
{
name => 'db_name',
# We can only get the glob itself. So we figure out its type this
# way, by trying first a scalar, then an array, then a hash.
#
- # The interesting thing is that this converts all deprecated
- # array or hash vars into hashrefs or arrayrefs, but that's
- # fine since as I write this all modern localconfig vars are
+ # The interesting thing is that this converts all deprecated
+ # array or hash vars into hashrefs or arrayrefs, but that's
+ # fine since as I write this all modern localconfig vars are
# actually scalars.
if (defined $$glob) {
$localconfig{$var} = $$glob;
# a 256-character string for site_wide_secret.
$value = undef if ($name eq 'site_wide_secret' and defined $value
and length($value) == 256);
-
+
if (!defined $value) {
push(@new_vars, $name);
$var->{default} = &{$var->{default}} if ref($var->{default}) eq 'CODE';
# Move any custom or old variables into a separate file.
if (scalar @old_vars) {
my $filename_old = "$filename.old";
- open(my $old_file, ">>:utf8", $filename_old)
+ open(my $old_file, ">>:utf8", $filename_old)
or die "$filename_old: $!";
local $Data::Dumper::Purity = 1;
foreach my $var (@old_vars) {
- print $old_file Data::Dumper->Dump([$localconfig->{$var}],
+ print $old_file Data::Dumper->Dump([$localconfig->{$var}],
["*$var"]) . "\n\n";
}
close $old_file;
=over
-=item C<$include_deprecated>
+=item C<$include_deprecated>
C<true> if you want the returned hashref to include *any* variable
-currently defined in localconfig, even if it doesn't exist in
-C<LOCALCONFIG_VARS>. Generally this is is only for use
+currently defined in localconfig, even if it doesn't exist in
+C<LOCALCONFIG_VARS>. Generally this is is only for use
by L</update_localconfig>.
=back
my ($string_id, $vars) = @_;
_cache()->{install_string_path} ||= template_include_path();
my $path = _cache()->{install_string_path};
-
+
my $string_template;
# Find the first template that defines this string.
foreach my $dir (@$path) {
if !defined $string_template;
last if defined $string_template;
}
-
+
die "No language defines the string '$string_id'"
if !defined $string_template;
# Used by template_include_path
sub _template_lang_directories {
my ($languages, $templatedir) = @_;
-
+
my @add = qw(custom default);
my $project = bz_locations->{'project'};
unshift(@add, $project) if $project;
my ($params) = @_;
my @used_languages = include_languages($params);
# Now, we add template directories in the order they will be searched:
- my $template_dirs = _template_base_directories();
+ my $template_dirs = _template_base_directories();
my @include_path;
foreach my $template_dir (@$template_dirs) {
- my @lang_dirs = _template_lang_directories(\@used_languages,
+ my @lang_dirs = _template_lang_directories(\@used_languages,
$template_dir);
# Hooks get each set of extension directories separately.
if ($params->{hook}) {
# during checksetup (since loading DBD::Oracle during checksetup when
# Oracle isn't installed causes a scary popup and pauses checksetup).
#
- # Win32::API ships with ActiveState by default, though there could
+ # Win32::API ships with ActiveState by default, though there could
# theoretically be a Windows installation without it, I suppose.
if (ON_WINDOWS and eval { require Win32::API }) {
# Call kernel32.SetErrorMode with arguments that mean:
# "The system does not display the critical-error-handler message box.
# Instead, the system sends the error to the calling process." and
# "A child process inherits the error mode of its parent process."
- my $SetErrorMode = Win32::API->new('kernel32', 'SetErrorMode',
+ my $SetErrorMode = Win32::API->new('kernel32', 'SetErrorMode',
'I', 'I');
my $SEM_FAILCRITICALERRORS = 0x0001;
my $SEM_NOGPFAULTERRORBOX = 0x0002;
=item C<include_languages>
-Used by L<Bugzilla::Template> to determine the languages' list which
-are compiled with the browser's I<Accept-Language> and the languages
+Used by L<Bugzilla::Template> to determine the languages' list which
+are compiled with the browser's I<Accept-Language> and the languages
of installed templates.
=item C<vers_cmp>
Bugzilla::Hook::process('job_map', { job_map => $job_map });
Bugzilla->request_cache->{job_map} = $job_map;
}
-
+
return Bugzilla->request_cache->{job_map};
}
# I don't see any way to do that in TheSchwartz.
ThrowCodeError('jobqueue_insert_failed', { job => $job, errmsg => $@ })
if !$retval;
-
+
return $retval;
}
$self->{_run_command} = 'subprocess_worker';
my $pidfile = $self->{gd_args}{pidfile};
if (!$pidfile) {
- $pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname}
+ $pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname}
. ".pid";
}
return (pidfile => $pidfile);
return $self->{'bug_count'} if defined $self->{'bug_count'};
($self->{'bug_count'}) =
Bugzilla->dbh->selectrow_array(
- 'SELECT COUNT(*) FROM keywords WHERE keywordid = ?',
+ 'SELECT COUNT(*) FROM keywords WHERE keywordid = ?',
undef, $self->id);
return $self->{'bug_count'};
}
my $class = shift;
my $dbh = Bugzilla->dbh;
my $keywords =
- $dbh->selectall_arrayref('SELECT '
+ $dbh->selectall_arrayref('SELECT '
. join(', ', $class->_get_db_columns) . ',
COUNT(keywords.bug_id) AS bug_count
FROM keyworddefs
if (!$keywords) {
return [];
}
-
+
foreach my $keyword (@$keywords) {
bless($keyword, $class);
}
Bugzilla::Keyword represents a keyword that can be added to a bug.
-This implements all standard C<Bugzilla::Object> methods. See
+This implements all standard C<Bugzilla::Object> methods. See
L<Bugzilla::Object> for more details.
=head1 METHODS
=over
-=item C<get_all_with_bug_count()>
+=item C<get_all_with_bug_count()>
Description: Returns all defined keywords. This is an efficient way
to get the associated bug counts, as only one SQL query
# *always* be the same for this Bugzilla, in every email,
# even if the admin changes the "ssl_redirect" parameter some day.
$email->header_set('X-Bugzilla-URL', Bugzilla->params->{'urlbase'});
-
+
# We add this header to mark the mail as "auto-generated" and
# thus to hopefully avoid auto replies.
$email->header_set('Auto-Submitted', 'auto-generated');
$hostname = $1;
$from .= "\@$hostname" if $from !~ /@/;
$email->header_set('From', $from);
-
+
# Sendmail adds a Date: header also, but others may not.
if (!defined $email->header('Date')) {
$email->header_set('Date', time2str("%a, %d %b %Y %T %z", time()));
push @args, Host => Bugzilla->params->{"smtpserver"},
username => Bugzilla->params->{"smtp_username"},
password => Bugzilla->params->{"smtp_password"},
- Hello => $hostname,
+ Hello => $hostname,
Debug => Bugzilla->params->{'smtp_debug'};
}
- Bugzilla::Hook::process('mailer_before_send',
+ Bugzilla::Hook::process('mailer_before_send',
{ email => $email, mailer_args => \@args });
# Allow for extensions to to drop the bugmail by clearing the 'to' header
else {
# This is useful for both Sendmail and Qmail, so we put it out here.
local $ENV{PATH} = SENDMAIL_PATH;
- my $mailer = Email::Send->new({ mailer => $mailer_class,
+ my $mailer = Email::Send->new({ mailer => $mailer_class,
mailer_args => \@args });
my $retval = $mailer->send($email);
ThrowCodeError('mail_send_error', { msg => $retval, mail => $email })
the ability to get, set, or clear entries from the cache.
The stored value must be an unblessed hashref, unblessed array ref, or a
-scalar.
+scalar.
All values are stored in the Memcached systems using the prefix configured with
the C<memcached_namespace> parameter, as well as an additional prefix managed
#
# If you are mapping to any custom fields in Bugzilla, you have to create
# the custom fields using Bugzilla Administration interface before you run
-# migrate.pl. However, if they are drop down or multi-select fields, you
+# migrate.pl. However, if they are drop down or multi-select fields, you
# don't have to populate the list of values--migrate.pl will do that for you.
# Some migrators create certain custom fields by default. If you see a
# field name starting with "cf_" on the right side of this configuration
default => 'local',
desc => <<'END',
# If migrate.pl comes across any dates without timezones, while doing the
-# migration, what timezone should we assume those dates are in?
+# migration, what timezone should we assume those dates are in?
# The best format for this variable is something like "America/Los Angeles".
# However, time zone abbreviations (like PST, PDT, etc.) are also acceptable,
# but will result in a less-accurate conversion of times and dates.
if ($self->config('starting_bug_id')) {
$dbh->bz_set_next_serial_value('bugs', 'bug_id',
$self->config('starting_bug_id'));
- }
+ }
$dbh->bz_start_transaction();
# Read Other Database
my $products = $self->products;
my $bugs = $self->bugs;
$self->after_read();
-
+
$self->translate_all_bugs($bugs);
Bugzilla->set_user(Bugzilla::User->super_user);
-
+
# Insert into Bugzilla
$self->before_insert();
$self->insert_users($users);
next if $field->is_abnormal;
$reset{$field->name} = 'id';
}
-
+
while (my ($table, $column) = each %reset) {
$dbh->bz_set_next_serial_value($table, $column);
}
-
+
$self->{serial_values_reset} = 1;
}
$other_fields{$field} = $value;
}
}
-
+
if (defined $original_status and !defined $bug{resolution}
and $self->map_value('bug_status_resolution', $original_status))
{
$bug{resolution} = $self->map_value('bug_status_resolution',
$original_status);
}
-
+
$bug{comment} = $self->_generate_description(\%bug, \%other_fields);
-
+
return wantarray ? (\%bug, \%other_fields) : \%bug;
}
sub _generate_description {
my ($self, $bug, $fields) = @_;
-
+
my $description = "";
foreach my $field (sort keys %$fields) {
next if grep($_ eq $field, $self->NON_COMMENT_FIELDS);
hour => $time[2],
minute => $time[1],
second => int($time[0]),
- time_zone => $tz,
+ time_zone => $tz,
});
$dt->set_time_zone(Bugzilla->local_timezone);
return $dt->iso8601;
sub translate_value {
my ($self, $field, $value) = @_;
-
+
if (!defined $value) {
warn("Got undefined value for $field\n");
$value = '';
}
-
+
if (ref($value) eq 'ARRAY') {
return [ map($self->translate_value($field, $_), @$value) ];
}
-
+
if (defined $self->map_value($field, $value)) {
return $self->map_value($field, $value);
}
-
+
if (grep($_ eq $field, USER_FIELDS)) {
if (defined $self->map_value('user', $value)) {
return $self->map_value('user', $value);
return undef if !$value;
return $self->parse_date($value);
}
-
+
return $value;
}
my ($self, $products) = @_;
foreach my $product (@$products) {
my $components = delete $product->{components};
-
+
my $created_prod = new Bugzilla::Product({ name => $product->{name} });
if (!$created_prod) {
$created_prod = Bugzilla::Product->create($product);
print get_text('migrate_product_created',
{ created => $created_prod }), "\n";
}
-
+
foreach my $component (@$components) {
next if new Bugzilla::Component({ product => $created_prod,
name => $component->{name} });
$product_values{$bug->{product}}->{$field}->{$bug->{$field}} = 1;
}
}
-
+
foreach my $field (@select_fields) {
next if $field->is_abnormal;
my $name = $field->name;
{ field => $field, value => $value }), "\n";
}
}
-
+
foreach my $product (keys %product_values) {
my $prod_obj = Bugzilla::Product->check($product);
foreach my $version (keys %{ $product_values{$product}->{version} }) {
print get_text('migrate_value_created', { product => $prod_obj,
field => $field,
value => $created->name }), "\n";
-
+
}
}
-
+
}
sub insert_bugs {
}
}
}
-
+
my $product = Bugzilla::Product->check($bug->{product});
-
+
# If this isn't a legal starting status, or if the bug has a
# resolution, then those will have to be set after creating the bug.
# We make them into objects so that we can normalize their names.
# accept. We're going to overwrite it immediately afterward.
$bug->{bug_status} = $default_status;
}
-
+
# If we're in dry-run mode, our custom fields haven't been created
# yet, so we shouldn't try to set them on creation.
if ($self->dry_run) {
delete $bug->{$field};
}
}
-
+
# File the bug as the reporter.
my $super_user = Bugzilla->user;
my $reporter = Bugzilla::User->check($bug->{reporter});
Bugzilla->set_user($super_user);
if (defined $bug->{creation_ts}) {
- $dbh->do('UPDATE bugs SET creation_ts = ?, delta_ts = ?
+ $dbh->do('UPDATE bugs SET creation_ts = ?, delta_ts = ?
WHERE bug_id = ?', undef, $bug->{creation_ts},
$bug->{creation_ts}, $created->id);
}
$dbh->do('UPDATE bugs SET resolution = ? WHERE bug_id = ?',
undef, $set_resolution->name, $created->id);
}
-
+
$self->_insert_comments($created, $comments);
$self->_insert_history($created, $history);
$self->_insert_attachments($created, $attachments);
=head2 translate_bug
-(Note: Normally you don't have to call this yourself, as
+(Note: Normally you don't have to call this yourself, as
C<Bugzilla::Migrate> does it for you.)
Uses the C<$translate_fields> and C<$translate_values> configuration variables
=head2 translate_field
-(Note: Normally you don't need to use this, because L</translate_bug>
+(Note: Normally you don't need to use this, because L</translate_bug>
handles it for you.)
Translates a field name in your bug-tracker to a field name in Bugzilla,
=head2 REQUIRED_MODULES
Returns an arrayref of Perl modules that must be installed in order
-for your migrator to run, in the same format as
+for your migrator to run, in the same format as
L<Bugzilla::Install::Requirements/REQUIRED_MODULES>.
=head2 CUSTOM_FIELDS
my $file = "$path/gnats-adm/categories";
$self->debug("Reading categories from $file");
- open(my $categories_fh, '<', $file) || die "$file: $!";
+ open(my $categories_fh, '<', $file) || die "$file: $!";
my @products;
foreach my $line (<$categories_fh>) {
$line = trim($line);
next if $line =~ /^#/;
my ($name, $description, $assigned_to, $cc) = split(':', $line, 4);
my %product = ( name => $name, description => $description );
-
+
my @initial_cc = split(',', $cc);
@initial_cc = @{ $self->translate_value('user', \@initial_cc) };
$assigned_to = $self->translate_value('user', $assigned_to);
# If this line starts a field name
if ($line =~ FIELD_REGEX) {
my ($new_field, $rest_of_line) = ($1, $2);
-
+
# If this is one of the last few PR fields, then make sure
# that we're getting our fields in the right order.
my $new_field_valid = 1;
my $current_field_pos = firstidx { $_ eq $search_for }
END_FIELD_ORDER;
if ($current_field_pos > -1) {
- my $new_field_pos = firstidx { $_ eq $new_field }
+ my $new_field_pos = firstidx { $_ eq $new_field }
END_FIELD_ORDER;
# We accept any field, as long as it's later than this one.
$new_field_valid = $new_field_pos > $current_field_pos ? 1 : 0;
}
-
+
if ($new_field_valid) {
if ($current_field) {
$fields{$current_field} = _handle_lines(\@value_lines);
}
$fields{$current_field} = _handle_lines(\@value_lines);
$fields{cc} = [$email->header('Cc')] if $email->header('Cc');
-
+
# If the Originator is invalid and we don't have a translation for it,
# use the From header instead.
my $originator = $self->translate_value('reporter', $fields{Originator},
bug_when => $bug->{delta_ts} || $bug->{creation_ts} });
}
$bug->{comments} = \@comments;
-
+
$bug->{component} = $self->config('component_name');
if (!$bug->{short_desc}) {
$bug->{short_desc} = NO_SUBJECT;
}
-
+
foreach my $attachment (@{ $bug->{attachments} || [] }) {
$attachment->{submitter} = $bug->{reporter};
$attachment->{creation_ts} = $bug->{creation_ts};
my ($self, $bug, $audit_trail) = @_;
return [] if !trim($audit_trail);
$self->debug(" Parsing audit trail...", 2);
-
+
if ($audit_trail !~ /^\S+-Changed-\S+:/ms) {
# This is just a comment from the bug's creator.
$self->debug(" Audit trail is just a comment.", 2);
return ([], $audit_trail);
}
-
+
my (@changes, %current_data, $current_column, $on_why);
my $extra_comment = '';
my $current_field;
my ($self, $changes, $old_field, $current_data) = @_;
$current_data->{field} = $old_field;
- $current_data->{removed} =
+ $current_data->{removed} =
$self->translate_value($old_field, $current_data->{removed});
$current_data->{added} =
$self->translate_value($old_field, $current_data->{added});
$self->debug($attachment, 3);
push(@attachments, $attachment);
}
-
+
if (scalar(@attachments) ne $num_attachments) {
warn "WARNING: Expected $num_attachments attachments but got "
. scalar(@attachments) . "\n" ;
$value =~ s/^.+?\b(\d[\w\.]+)\b.+$/$1/;
}
}
-
+
my @args = @_;
$args[1] = $value;
-
+
$value = $self->SUPER::translate_value(@args);
return $value if ref $value;
-
+
if (grep($_ eq $field, $self->USER_FIELDS)) {
my $from_value = $value;
$value = $self->user_to_email($value);
$self->add_user($from_value, $value);
}
}
-
+
return $value;
}
SELECT $columns FROM $table
WHERE $id_field = ?}, undef, $id);
} else {
- unless (defined $param->{name} || (defined $param->{'condition'}
+ unless (defined $param->{name} || (defined $param->{'condition'}
&& defined $param->{'values'}))
{
ThrowCodeError('bad_arg', { argument => 'param',
elsif (defined $param->{'condition'} && defined $param->{'values'}) {
caller->isa('Bugzilla::Object')
|| ThrowCodeError('protection_violation',
- { caller => caller,
+ { caller => caller,
function => $class . '::new',
argument => 'condition/values' });
$condition = $param->{'condition'};
my (@terms, @values, $postamble);
foreach my $field (keys %$criteria) {
my $value = $criteria->{$field};
-
+
# allow for LIMIT and OFFSET expressions via the criteria.
next if $field eq 'OFFSET';
if ( $field eq 'LIMIT' ) {
next unless defined $value;
detaint_natural($value)
- or ThrowCodeError('param_must_be_numeric',
- { param => 'LIMIT',
+ or ThrowCodeError('param_must_be_numeric',
+ { param => 'LIMIT',
function => "${class}::match" });
my $offset;
if (defined $criteria->{OFFSET}) {
$offset = $criteria->{OFFSET};
detaint_signed($offset)
- or ThrowCodeError('param_must_be_numeric',
+ or ThrowCodeError('param_must_be_numeric',
{ param => 'OFFSET',
function => "${class}::match" });
}
# value (either a scalar or an array of values).
foreach my $k (keys %$value) {
push(@terms, $k);
- my @this_value = ref($value->{$k}) ? @{ $value->{$k} }
+ my @this_value = ref($value->{$k}) ? @{ $value->{$k} }
: ($value->{$k});
push(@values, @this_value);
- }
+ }
next;
}
# This method is protected. It's used to help implement set_ functions.
my $caller = caller;
$caller->isa('Bugzilla::Object') || $caller->isa('Bugzilla::Extension')
- || ThrowCodeError('protection_violation',
+ || ThrowCodeError('protection_violation',
{ caller => caller,
superclass => __PACKAGE__,
function => 'Bugzilla::Object->set' });
}
$self->$method($field_values{$key}, \%field_values);
}
- Bugzilla::Hook::process('object_end_of_set_all',
+ Bugzilla::Hook::process('object_end_of_set_all',
{ object => $self, params => \%field_values });
}
if (!defined $new || !defined $old) {
next if !defined $new && !defined $old;
}
- elsif ( ($numeric{$column} && $old == $new)
+ elsif ( ($numeric{$column} && $old == $new)
|| ($date{$column} && str2time($old) == str2time($new))
|| $old eq $new ) {
next;
my $columns = join(', ', map {"$_ = ?"} @update_columns);
- $dbh->do("UPDATE $table SET $columns WHERE $id_field = ?", undef,
+ $dbh->do("UPDATE $table SET $columns WHERE $id_field = ?", undef,
@values, $self->id) if @values;
Bugzilla::Hook::process('object_end_of_update',
my $user_id = Bugzilla->user->id || undef;
my $sth = $dbh->prepare(
'INSERT INTO audit_log (user_id, class, object_id, field,
- removed, added, at_time)
+ removed, added, at_time)
VALUES (?,?,?,?,?,?,LOCALTIMESTAMP(0))');
# During creation or removal, $changes is actually just a string
# indicating whether we're creating or removing the object.
# We do this thing with NAME_FIELD because $self->name returns
# the wrong thing for Bugzilla::User.
my $name = $self->{$self->NAME_FIELD};
- my @added_removed = $changes eq AUDIT_CREATE ? (undef, $name)
+ my @added_removed = $changes eq AUDIT_CREATE ? (undef, $name)
: ($name, undef);
$sth->execute($user_id, $class, $self->id, $changes, @added_removed);
return;
my ($field, $insert_me, $dependencies, $result, $loop_tracking) = @_;
if ($loop_tracking->{$field}) {
- ThrowCodeError('object_dep_sort_loop',
- { field => $field,
+ ThrowCodeError('object_dep_sort_loop',
+ { field => $field,
considered => [keys %$loop_tracking] });
}
$loop_tracking->{$field} = 1;
# We copy this into a hash so that the hook doesn't modify the constant.
# (That could be bad in mod_perl.)
my %validators = %{ $invocant->VALIDATORS };
- Bugzilla::Hook::process('object_validators',
+ Bugzilla::Hook::process('object_validators',
{ class => $class, validators => \%validators });
$cache->{$cache_key} = \%validators;
return $cache->{$cache_key};
if ($def->{NOTNULL} and !defined $def->{DEFAULT}
# SERIAL fields effectively have a DEFAULT, but they're not
# listed as having a DEFAULT in DB::Schema.
- and $def->{TYPE} !~ /serial/i)
+ and $def->{TYPE} !~ /serial/i)
{
my $field = $class->REQUIRED_FIELD_MAP->{$column} || $column;
push(@required, $field);
The names of the columns that you want to read out of the database
and into this object. This should be an array.
-I<Note>: Though normally you will never need to access this constant's data
+I<Note>: Though normally you will never need to access this constant's data
directly in your subclass, if you do, you should access it by calling the
C<_get_db_columns> method instead of accessing the constant directly. (The
only exception to this rule is calling C<SUPER::DB_COLUMNS> from within
The name of the column that should be considered to be the unique
"name" of this object. The 'name' is a B<string> that uniquely identifies
-this Object in the database. Defaults to 'name'. When you specify
-C<< {name => $name} >> to C<new()>, this is the column that will be
+this Object in the database. Defaults to 'name'. When you specify
+C<< {name => $name} >> to C<new()>, this is the column that will be
matched against in the DB.
=item C<ID_FIELD>
=item C<VALIDATORS>
A hashref that points to a function that will validate each param to
-L</create>.
+L</create>.
Validators are called both by L</create> and L</set>. When
they are called by L</create>, the first argument will be the name
When they are called by L</set>, the first argument will be
a reference to the current object (what we normally call C<$self>).
-The second argument will be the value passed to L</create> or
-L</set>for that field.
+The second argument will be the value passed to L</create> or
+L</set>for that field.
The third argument will be the name of the field being validated.
This may be required by validators which validate several distinct fields.
caught as being required. However, in reality it defaults to the
component's initial_qa_contact.)
-=item *
+=item *
Fields that have defaults that should be set by validators, but are
actually stored in a table different from L</DB_TABLE> (like the "cc"
field for bugs, which defaults to the "initialcc" of the Component, but won't
-be caught as a normal required field because it's in a separate table.)
+be caught as a normal required field because it's in a separate table.)
=back
=item B<Params>
-If you pass an integer, the integer is the id of the object,
+If you pass an integer, the integer is the id of the object,
from the database, that we want to read in. (id is defined
as the value in the L</ID_FIELD> column).
-If you pass in a hashref, you can pass a C<name> key. The
-value of the C<name> key is the case-insensitive name of the object
+If you pass in a hashref, you can pass a C<name> key. The
+value of the C<name> key is the case-insensitive name of the object
(from L</NAME_FIELD>) in the DB. You can also pass in an C<id> key
-which will be interpreted as the id of the object you want (overriding the
+which will be interpreted as the id of the object you want (overriding the
C<name> key).
B<Additional Parameters Available for Subclasses>
Params: \@id_list - A reference to an array of numbers, database ids.
If any of these are not numeric, the function
will throw an error. If any of these are not
- valid ids in the database, they will simply
+ valid ids in the database, they will simply
be skipped.
Returns: A reference to an array of objects.
=item B<Params>
-A hashref, where the keys are column names of the table, pointing to the
-value that you want to match against for that column.
+A hashref, where the keys are column names of the table, pointing to the
+value that you want to match against for that column.
There are two special values, the constants C<NULL> and C<NOT_NULL>,
which means "give me objects where this field is NULL or NOT NULL,
respectively."
In addition to the column keys, there are a few special keys that
-can be used to rig the underlying database queries. These are
+can be used to rig the underlying database queries. These are
C<LIMIT>, C<OFFSET>, and C<WHERE>.
-The value for the C<LIMIT> key is expected to be an integer defining
+The value for the C<LIMIT> key is expected to be an integer defining
the number of objects to return, while the value for C<OFFSET> defines
-the position, relative to the number of objects the query would normally
-return, at which to begin the result set. If C<OFFSET> is defined without
+the position, relative to the number of objects the query would normally
+return, at which to begin the result set. If C<OFFSET> is defined without
a corresponding C<LIMIT> it is silently ignored.
The C<WHERE> key provides a mechanism for adding arbitrary WHERE
-clauses to the underlying query. Its value is expected to a hash
-reference whose keys are the columns, operators and placeholders, and the
+clauses to the underlying query. Its value is expected to a hash
+reference whose keys are the columns, operators and placeholders, and the
values are the placeholders' bind value. For example:
WHERE => { 'some_column >= ?' => $some_value }
type in the database.
Subclass Implementors:
- This function basically just calls
+ This function basically just calls
L</check_required_create_fields>, then
L</run_create_validators>, and then finally
L</insert_create_data>. So if you have a complex system that
Part of L</create>.
Takes the return value from L</run_create_validators> and inserts the
-data into the database. Returns a newly created object.
+data into the database. Returns a newly created object.
=item C<update>
Returns: A list of objects, or an empty list if there are none.
- Notes: Note that you must call this as $class->get_all. For
- example, Bugzilla::Keyword->get_all.
+ Notes: Note that you must call this as $class->get_all. For
+ example, Bugzilla::Keyword->get_all.
Bugzilla::Keyword::get_all will not work.
=back
Files with different licenses or copyright holders:
-=over
+=over
=item F<lib/PatchReader/CVSClient.pm>
if (!$this->{FILE_STARTED}) {
$this->_start_file();
}
-
+
# Send end notification and set state
$this->{TARGET}->end_file($this->{FILE_STATE});
delete $this->{FILE_STATE};
my $params = $class->run_create_validators(@_);
# Some fields do not exist in the DB as is.
if (defined $params->{classification}) {
- $params->{classification_id} = delete $params->{classification};
+ $params->{classification_id} = delete $params->{classification};
}
my $version = delete $params->{version};
my $create_series = delete $params->{create_series};
# Add the new version and milestone into the DB as valid values.
Bugzilla::Version->create({ value => $version, product => $product });
- Bugzilla::Milestone->create({ value => $product->default_milestone,
+ Bugzilla::Milestone->create({ value => $product->default_milestone,
product => $product });
# Create groups and series for the new product, if requested.
canedit, editcomponents, editbugs, canconfirm
FROM groups
LEFT JOIN group_control_map
- ON id = group_id
+ ON id = group_id
$where_or_and product_id = ?
$and_or_where isbuggroup = 1};
- $self->{group_controls} =
+ $self->{group_controls} =
$dbh->selectall_hashref($query, 'id', undef, $self->id);
# For each group ID listed above, create and store its group object.
FROM group_control_map
INNER JOIN groups ON group_control_map.group_id = groups.id
WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ?
- AND (othercontrol = $shown OR othercontrol = $default)",
+ AND (othercontrol = $shown OR othercontrol = $default)",
{Columns=>[1,2]}, $self->id) };
# If the user is a member, then we use the membercontrol value.
# is Mandatory, the group is Mandatory for everybody, regardless of their
# group membership.
my $ids = Bugzilla->dbh->selectcol_arrayref(
- "SELECT group_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
sub groups_valid {
my ($self) = @_;
return $self->{groups_valid} if defined $self->{groups_valid};
-
+
# Note that we don't check OtherControl below, because there is no
# valid NA/* combination.
my $ids = Bugzilla->dbh->selectcol_arrayref(
my $ids = $dbh->selectcol_arrayref(q{
SELECT id FROM milestones
WHERE product_id = ?}, undef, $self->id);
-
+
$self->{milestones} = Bugzilla::Milestone->new_from_list($ids);
}
return $self->{milestones};
my $dbh = Bugzilla->dbh;
if (!defined $self->{'bug_ids'}) {
- $self->{'bug_ids'} =
+ $self->{'bug_ids'} =
$dbh->selectcol_arrayref(q{SELECT bug_id FROM bugs
WHERE product_id = ?},
undef, $self->id);
of L<Bugzilla::Object>, and thus provides all methods that
L<Bugzilla::Object> provides.
-The methods that are specific to C<Bugzilla::Product> are listed
+The methods that are specific to C<Bugzilla::Product> are listed
below.
=head1 METHODS
is also returned. Moreover, bug groups which have no
special settings for the product are also returned.
- Returns: A hash with group id as key and hash containing
+ Returns: A hash with group id as key and hash containing
a Bugzilla::Group object and the properties of group
relative to the product.
=item C<groups_available>
-Tells you what groups are set to Default or Shown for the
+Tells you what groups are set to Default or Shown for the
currently-logged-in user (taking into account both OtherControl and
MemberControl). Returns an arrayref of L<Bugzilla::Group> objects with
an extra hash keys set, C<is_default>, which is true if the group
If the 2nd argument passed to C<preload> is true, flag types for these
products and their components are also preloaded.
-This function is not exported, so must be called like
+This function is not exported, so must be called like
C<Bugzilla::Product::preload($products)>.
=back
# Don't require existing public/private keypairs.
use constant CRYPT_VERIFYCONTEXT => 0xF0000000;
-# For some reason, BOOLEAN doesn't work properly as a return type with
+# For some reason, BOOLEAN doesn't work properly as a return type with
# Win32::API.
use constant RTLGENRANDOM_PROTO => <<END;
INT SystemFunction036(
my $buffer;
$fh->read($buffer, SEED_SIZE);
if (length($buffer) < SEED_SIZE) {
- die "Could not read enough seed bytes from $from, got only "
+ die "Could not read enough seed bytes from $from, got only "
. length($buffer);
}
$fh->close;
\. # Followed possibly by some decimal places
(?:\d*)
)?
-
+
(?: # Followed possibly by an exponent.
[Ee]
[+-]?
notsubstring => \&_multiselect_negative,
nowords => \&_multiselect_negative,
nowordssubstr => \&_multiselect_negative,
-
+
allwords => \&_multiselect_multiple,
allwordssubstr => \&_multiselect_multiple,
anyexact => \&_multiselect_multiple,
anywords => \&_multiselect_multiple,
anywordssubstr => \&_multiselect_multiple,
-
+
_non_changed => \&_multiselect_nonchanged,
};
_non_changed => \&_user_nonchanged,
},
'setters.login_name' => {
- _non_changed => \&_user_nonchanged,
+ _non_changed => \&_user_nonchanged,
},
qa_contact => {
_non_changed => \&_user_nonchanged,
},
-
+
# General Bug Fields
alias => { _non_changed => \&_nullable },
# We check all attachment fields against this.
FIELD_TYPE_DATE, { _non_changed => \&_nullable_date },
FIELD_TYPE_TEXTAREA, { _non_changed => \&_nullable },
FIELD_TYPE_MULTI_SELECT, MULTI_SELECT_OVERRIDE,
- FIELD_TYPE_BUG_URLS, MULTI_SELECT_OVERRIDE,
+ FIELD_TYPE_BUG_URLS, MULTI_SELECT_OVERRIDE,
};
# These are fields where special action is taken depending on the
return $joins;
};
-# This constant defines the columns that can be selected in a query
+# This constant defines the columns that can be selected in a query
# and/or displayed in a bug list. Column records include the following
# fields:
#
# that returns the value of the column);
#
# 3. title: The title of the column as displayed to users.
-#
+#
# Note: There are a few hacks in the code that deviate from these definitions.
# In particular, the redundant short_desc column is removed when the
# client requests "all" columns.
. " ELSE FLOOR(100 * (map_actual_time.total / $total_time))"
. " END)",
- 'flagtypes.name' => $dbh->sql_group_concat('DISTINCT '
+ 'flagtypes.name' => $dbh->sql_group_concat('DISTINCT '
. $dbh->sql_string_concat('map_flagtypes.name', 'map_flags.status')),
'keywords' => $dbh->sql_group_concat('DISTINCT map_keyworddefs.name'),
blocked => $dbh->sql_group_concat('DISTINCT map_blocked.blocked'),
dependson => $dbh->sql_group_concat('DISTINCT map_dependson.dependson'),
-
+
'longdescs.count' => 'COUNT(DISTINCT map_longdescs_count.comment_id)',
last_visit_ts => 'bug_user_last_visit.last_visit_ts',
bug_interest_ts => 'bug_interest.modification_time',
# Backward-compatibility for old field names. Goes new_name => old_name.
# These are here and not in _translate_old_column because the rest of the
# code actually still uses the old names, while the fielddefs table uses
- # the new names (which is not the case for the fields handled by
+ # the new names (which is not the case for the fields handled by
# _translate_old_column).
my %old_names = (
creation_ts => 'opendate',
# There's no reason to support reporting on unique fields.
# Also, some other fields don't make very good reporting axises,
# or simply don't work with the current reporting system.
- my @no_report_columns =
+ my @no_report_columns =
qw(bug_id alias short_short_desc opendate changeddate
flagtypes.name keywords relevance);
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
-
+
my $self = { @_ };
bless($self, $class);
$self->{'user'} ||= Bugzilla->user;
-
+
# There are certain behaviors of the CGI "Vars" hash that we don't want.
# In particular, if you put a single-value arrayref into it, later you
# get back out a string, which breaks anyexact charts (because they
sub _special_order {
my ($self) = @_;
return $self->{special_order} if $self->{special_order};
-
+
my %special_order = %{ SPECIAL_ORDER() };
my $select_fields = Bugzilla->fields({ type => FIELD_TYPE_SINGLE_SELECT });
foreach my $field (@$select_fields) {
my ($self, $order_by_item) = @_;
my ($field, $direction) = split_order_term($order_by_item);
-
+
$direction = '' if lc($direction) eq 'asc';
my $special_order = $self->_special_order->{$field}->{order};
# Standard fields have underscores in their SELECT alias instead
my ($self) = @_;
my $limit = $self->_params->{limit};
my $offset = $self->_params->{offset};
-
+
my $max_results = Bugzilla->params->{'max_search_results'};
if (!$self->{allow_unlimited} && (!$limit || $limit > $max_results)) {
$limit = $max_results;
}
-
+
if (defined($offset) && !$limit) {
$limit = INT_MAX;
}
if (defined $limit) {
- detaint_natural($limit)
- || ThrowCodeError('param_must_be_numeric',
+ detaint_natural($limit)
+ || ThrowCodeError('param_must_be_numeric',
{ function => 'Bugzilla::Search::new',
param => 'limit' });
if (defined $offset) {
or die "Not all same-named joins have identical 'to': "
. Dumper($join, $others_like_me);
}
-
+
# We don't need to call uniq here--translate_join will do that
# for us.
my @conditions = map { @{ $_->{extra} || [] } }
}
push(@result, $join);
}
-
+
return @result;
}
if ($user->id) {
$security_join->{extra} =
["NOT (" . $user->groups_in_sql('security_map.group_id') . ")"];
-
+
my $security_cc_join = {
table => 'cc',
as => 'security_cc',
};
push(@joins, $security_cc_join);
}
-
+
return @joins;
}
# This takes a join data structure and turns it into actual JOIN SQL.
sub _translate_join {
my ($self, $join_info) = @_;
-
+
die "join with no table: " . Dumper($join_info) if !$join_info->{table};
die "join with no 'as': " . Dumper($join_info) if !$join_info->{as};
my $join = $join_info->{join} || 'LEFT';
my @extra = @{ $join_info->{extra} || [] };
$name =~ s/\./_/g;
-
+
# If a term contains ORs, we need to put parens around the condition.
# This is a pretty weak test, but it's actually OK to put parens
# around too many things.
# until their group controls are set. So if a bug has a NULL creation_ts,
# it shouldn't show up in searches at all.
my @where = ('bugs.creation_ts IS NOT NULL');
-
+
my $security_term = 'security_map.group_id IS NULL';
my $user = $self->_user;
push(@extra_group_by, $sql);
}
- # And all items from ORDER BY must be in the GROUP BY. The above loop
+ # And all items from ORDER BY must be in the GROUP BY. The above loop
# doesn't catch items that were put into the ORDER BY from SPECIAL_ORDER.
foreach my $column ($self->_valid_order_columns) {
my $special_order = $self->_special_order->{$column}->{order};
next if !$special_order;
push(@extra_group_by, @$special_order);
}
-
+
@extra_group_by = uniq @extra_group_by;
-
+
# bug_id is the only field we actually group by.
return ('bugs.bug_id', join(',', @extra_group_by));
}
sub _convert_old_params {
my ($self) = @_;
my $params = $self->_params;
-
+
# bugidtype has different values in modern Search.pm.
if (defined $params->{'bugidtype'}) {
my $value = $params->{'bugidtype'};
$params->{'bugidtype'} = $value eq 'exclude' ? 'nowords' : 'anyexact';
}
-
+
foreach my $old_name (keys %{ FIELD_MAP() }) {
if (defined $params->{$old_name}) {
my $new_name = FIELD_MAP->{$old_name};
my ($self) = @_;
my $params = $self->_params;
my $chart_fields = $self->_chart_fields;
-
+
my $clause = new Bugzilla::Search::Clause();
foreach my $field_name (keys %$chart_fields) {
# CGI params shouldn't have periods in them, so we only accept
sub _special_parse_chfield {
my ($self) = @_;
my $params = $self->_params;
-
+
my $date_from = trim(lc($params->{'chfieldfrom'} || ''));
my $date_to = trim(lc($params->{'chfieldto'} || ''));
$date_from = '' if $date_from eq 'now';
my ($self) = @_;
return if !$self->_user->is_timetracker;
my $params = $self->_params;
-
+
my $clause = new Bugzilla::Search::Clause();
if (my $from = $params->{'deadlinefrom'}) {
$clause->add('deadline', 'greaterthaneq', $from);
if (my $to = $params->{'deadlineto'}) {
$clause->add('deadline', 'lessthaneq', $to);
}
-
+
return $clause;
}
sub _special_parse_email {
my ($self) = @_;
my $params = $self->_params;
-
+
my @email_params = grep { $_ =~ /^email\d+$/ } keys %$params;
-
+
my $clause = new Bugzilla::Search::Clause();
foreach my $param (@email_params) {
$param =~ /(\d+)$/;
if ($params->{"emaillongdesc$id"}) {
$or_clause->add("commenter", $type, $email);
}
-
+
$clause->add($or_clause);
}
-
+
return $clause;
}
sub _charts_to_conditions {
my ($self) = @_;
-
+
my $clause = $self->_charts;
my @joins;
$clause->walk_conditions(sub {
sub _charts {
my ($self) = @_;
-
+
my $clause = $self->_params_to_data_structure();
my $chart_id = 0;
$clause->walk_conditions(sub { $self->_handle_chart($chart_id++, @_) });
sub _params_to_data_structure {
my ($self) = @_;
-
+
# First we get the "special" charts, representing all the normal
# fields on the search page. This may modify _params, so it needs to
# happen first.
# Then we process the old Boolean Charts input format.
$clause->add( $self->_boolean_charts );
-
+
# And then process the modern "custom search" format.
$clause->add( $self->_custom_search );
sub _boolean_charts {
my ($self) = @_;
-
+
my $params = $self->_params;
my @param_list = keys %$params;
-
+
my @all_field_params = grep { /^field-?\d+/ } @param_list;
my @chart_ids = map { /^field(-?\d+)/; $1 } @all_field_params;
@chart_ids = sort { $a <=> $b } uniq @chart_ids;
-
+
my $clause = new Bugzilla::Search::Clause();
foreach my $chart_id (@chart_ids) {
my @all_and = grep { /^field$chart_id-\d+/ } @param_list;
my @and_ids = map { /^field$chart_id-(\d+)/; $1 } @all_and;
@and_ids = sort { $a <=> $b } uniq @and_ids;
-
+
my $and_clause = new Bugzilla::Search::Clause();
foreach my $and_id (@and_ids) {
my @all_or = grep { /^field$chart_id-$and_id-\d+/ } @param_list;
my @or_ids = map { /^field$chart_id-$and_id-(\d+)/; $1 } @all_or;
@or_ids = sort { $a <=> $b } uniq @or_ids;
-
+
my $or_clause = new Bugzilla::Search::Clause('OR');
foreach my $or_id (@or_ids) {
my $identifier = "$chart_id-$and_id-$or_id";
}
$clause->add($and_clause);
}
-
+
return $clause;
}
if !$current_clause;
next;
}
-
+
my $operator = $params->{"o$id"};
my $value = $params->{"v$id"};
# no-value operators ignore the value, however a value needs to be set
$condition->negate($params->{"n$id"});
$current_clause->add($condition);
}
-
+
# We allow people to specify more OPs than CPs, so at the end of the
# loop our top clause may be still in the stack instead of being
# $current_clause.
my ($self) = @_;
my $params = $self->_params;
my @param_list = keys %$params;
-
+
my @field_params = grep { /^f\d+$/ } @param_list;
my @field_ids = map { /(\d+)/; $1 } @field_params;
@field_ids = sort { $a <=> $b } @field_ids;
my ($field, $operator, $value) = $condition->fov;
return if (!defined $field or !defined $operator or !defined $value);
$field = FIELD_MAP->{$field} || $field;
-
+
my $string_value;
if (ref $value eq 'ARRAY') {
# Trim input and ignore blank values.
return if $value eq '';
$string_value = $value;
}
-
+
$self->_chart_fields->{$field}
or ThrowCodeError("invalid_field_name", { field => $field });
trick_taint($field);
-
+
# This is the field as you'd reference it in a SQL statement.
my $full_field = $field =~ /\./ ? $field : "bugs.$field";
#
# all_values is for search functions that do operate
# on multiple values, like anyexact.
-
+
my %search_args = (
chart_id => $chart_id,
sequence => $chart_id,
# All the things here that don't get pulled out of
# %search_args are their original values before
- # do_search_function modified them.
+ # do_search_function modified them.
$self->search_description({
field => $field, type => $operator,
value => $string_value, term => $search_args{term},
sub do_search_function {
my ($self, $args) = @_;
my ($field, $operator) = @$args{qw(field operator)};
-
+
if (my $parse_func = SPECIAL_PARSING->{$field}) {
$self->$parse_func($args);
# Some parsing functions set $term, though most do not.
# parsing.
return if $args->{term};
}
-
+
my $operator_field_override = $self->_get_operator_field_override();
my $override = $operator_field_override->{$field};
# Attachment fields get special handling, if they don't have a specific
my $field_obj = $self->_chart_fields->{$field};
$override = $operator_field_override->{$field_obj->type};
}
-
+
if ($override) {
my $search_func = $self->_pick_override_function($override, $operator);
$self->$search_func($args) if $search_func;
if (!defined $args->{term}) {
$self->_do_operator_function($args);
}
-
+
if (!defined $args->{term}) {
# This field and this type don't work together. Generally,
# this should never be reached, because it should be handled
my $self = shift;
my $cache = Bugzilla->request_cache;
- return $cache->{operator_field_override}
+ return $cache->{operator_field_override}
if defined $cache->{operator_field_override};
my %operator_field_override = %{ OPERATOR_FIELD_OVERRIDE() };
Bugzilla::Hook::process('search_operator_field_override',
- { search => $self,
+ { search => $self,
operators => \%operator_field_override });
$cache->{operator_field_override} = \%operator_field_override;
$value = $args->{value};
}
my ($field, $operator) = @$args{qw(field operator)};
-
+
my $numeric_operator = !grep { $_ eq $operator } NON_NUMERIC_OPERATORS;
my $numeric_field = $self->_chart_fields->{$field}->is_numeric;
my $numeric_value = ($value =~ NUMBER_REGEX) ? 1 : 0;
$split_on ||= qr/[\s,]+/;
my $dbh = Bugzilla->dbh;
my $all_values = $args->{all_values};
-
+
my @array;
if (ref $all_values eq 'ARRAY') {
@array = @$all_values;
@array = map { trim($_) } @array;
@array = grep { defined $_ and $_ ne '' } @array;
}
-
+
if ($args->{field} eq 'resolution') {
@array = map { $_ eq '---' ? '' : $_ } @array;
}
-
+
return @array;
}
sub _word_terms {
my ($self, $args) = @_;
my $dbh = Bugzilla->dbh;
-
+
my @values = split(/[\s,]+/, $args->{value});
@values = grep { defined $_ and $_ ne '' } @values;
my @substring_terms = $self->_substring_terms($args);
-
+
my @terms;
my $start = $dbh->WORD_START;
my $end = $dbh->WORD_END;
my $substring_term = shift @substring_terms;
push(@terms, "$substring_term AND $regex_term");
}
-
+
return @terms;
}
elsif ($unit eq 'y') {
if ($startof) {
return sprintf("%4d-01-01 00:00:00", $year+1900-$amount);
- }
+ }
else {
- return sprintf("%4d-%02d-%02d %02d:%02d:%02d",
+ return sprintf("%4d-%02d-%02d %02d:%02d:%02d",
$year+1900-$amount, $month+1, $mday, $hour, $min, $sec);
}
}
return sprintf("%4d-%02d-01 00:00:00", $year+1900, $month+1);
}
else {
- return sprintf("%4d-%02d-%02d %02d:%02d:%02d",
+ return sprintf("%4d-%02d-%02d %02d:%02d:%02d",
$year+1900, $month+1, $mday, $hour, $min, $sec);
}
}
# Special case for 'beginning of an hour'
if ($startof) {
$fmt = "%Y-%m-%d %H:00:00";
- }
+ }
$date -= 3600*$amount;
return time2str($fmt, $date);
}
@$args{qw(value operator field chart_id joins)};
my $dbh = Bugzilla->dbh;
my $user = $self->_user;
-
+
# We already know $value will match this regexp, else we wouldn't be here.
$value =~ /\%group\.([^%]+)%/;
my $group_name = $1;
@$args{qw(chart_id sequence joins operator value)};
my $user = $self->_user;
my $dbh = Bugzilla->dbh;
-
+
$value =~ m/%group\.([^%]+)%/;
my $group = Bugzilla::Group->check({ name => $1, _error => 'invalid_group_name' });
$group->check_members_are_visible();
$chart_id = "CC$$sequence";
$args->{sequence}++;
}
-
+
my $cc_table = "cc_$chart_id";
push(@$joins, { table => 'cc', as => $cc_table });
my $group_table = "user_group_map_$chart_id";
# Needed for setters.login_name and requestees.login_name.
# Otherwise when we try to join "profiles" below, we'd get
# something like "setters.login_name.login_name" in the "from".
- $as =~ s/\./_/g;
+ $as =~ s/\./_/g;
# This helps implement the email1, email2, etc. parameters.
if ($chart_id =~ /default/) {
$as .= "_$sequence";
push(@$joins, $join);
$args->{full_field} = "$as.login_name";
}
-
+
# We COALESCE fields that can be NULL, to make "not"-style operators
# continue to work properly. For example, "qa_contact is not equal to bob"
# should also show bugs where the qa_contact is NULL. With COALESCE,
if ($is_nullable) {
$args->{full_field} = "COALESCE($args->{full_field}, $null_alternate)";
}
-
+
# For fields whose values are stored in other tables, negation (NOT)
# only works properly if we put the condition into the JOIN instead
# of the WHERE.
# Using the last join works properly whether we're searching based
# on userid or login_name.
my $last_join = $joins->[-1];
-
+
# For negative operators, the system we're using here
# only works properly if we reverse the operator and check IS NULL
# in the WHERE.
}
$self->_do_operator_function($args);
push(@{ $last_join->{extra} }, $args->{term});
-
+
# For login_name searches, we only want a single join.
# So we create a subselect table out of our two joins. This makes
# negation (NOT) work properly for values that are in other
$last_join->{join} = 'INNER';
my ($join_sql) = $self->_translate_join($last_join);
my $first_join = $joins->[-1];
- my $as = $first_join->{as};
+ my $as = $first_join->{as};
my $table = $first_join->{table};
my $columns = "bug_id";
$columns .= ",isprivate" if @{ $first_join->{extra} };
$first_join->{as} = $new_as;
$last_join = $first_join;
}
-
+
# If we're joining the first table (we're using a pronoun and
# searching by user id) then we need to check $other_table->{field}.
my $check_field = $last_join->{as} . '.bug_id';
sub _long_desc_changedby {
my ($self, $args) = @_;
my ($chart_id, $joins, $value) = @$args{qw(chart_id joins value)};
-
+
my $table = "longdescs_$chart_id";
push(@$joins, { table => 'longdescs', as => $table });
my $user_id = login_to_id($value, THROW_ERROR);
my ($chart_id, $operator, $value, $joins) =
@$args{qw(chart_id operator value joins)};
my $dbh = Bugzilla->dbh;
-
+
my $sql_operator = ($operator =~ /before/) ? '<=' : '>=';
my $table = "longdescs_$chart_id";
my $sql_date = $dbh->quote(SqlifyDate($value));
my $comments_col = "comments";
$comments_col = "comments_noprivate" unless $self->_user->is_insider;
push(@$joins, { table => 'bugs_fulltext', as => $table });
-
+
# Create search terms to add to the SELECT and WHERE clauses.
my ($term1, $rterm1) =
$dbh->sql_fulltext_search("$table.$comments_col", $value);
$term = "NOT($term)";
}
$args->{term} = $term;
-
+
# In order to sort by relevance (in case the user requests it),
# we SELECT the relevance value so we can add it to the ORDER BY
- # clause. Every time a new fulltext chart isadded, this adds more
+ # clause. Every time a new fulltext chart isadded, this adds more
# terms to the relevance sql.
#
# We build the relevance SQL by modifying the COLUMNS list directly,
sub _work_time_changedby {
my ($self, $args) = @_;
my ($chart_id, $joins, $value) = @$args{qw(chart_id joins value)};
-
+
my $table = "longdescs_$chart_id";
push(@$joins, { table => 'longdescs', as => $table });
my $user_id = login_to_id($value, THROW_ERROR);
my ($chart_id, $operator, $value, $joins) =
@$args{qw(chart_id operator value joins)};
my $dbh = Bugzilla->dbh;
-
+
my $table = "longdescs_$chart_id";
my $sql_operator = ($operator =~ /before/) ? '<=' : '>=';
my $sql_date = $dbh->quote(SqlifyDate($value));
"$table.bug_when $sql_operator $sql_date"],
};
push(@$joins, $join);
-
+
$args->{term} = "$table.bug_when IS NOT NULL";
}
sub _percentage_complete {
my ($self, $args) = @_;
-
+
$args->{full_field} = $self->COLUMNS->{percentage_complete}->{name};
# We need actual_time in _select_columns, otherwise we can't use
sub _days_elapsed {
my ($self, $args) = @_;
my $dbh = Bugzilla->dbh;
-
+
$args->{full_field} = "(" . $dbh->sql_to_days('NOW()') . " - " .
$dbh->sql_to_days('bugs.delta_ts') . ")";
}
sub _component_nonchanged {
my ($self, $args) = @_;
-
+
$args->{full_field} = "components.name";
$self->_do_operator_function($args);
my $term = $args->{term};
sub _classification_nonchanged {
my ($self, $args) = @_;
my $joins = $args->{joins};
-
+
# This joins the right tables for us.
$self->_add_extra_column('product');
-
- # Generate the restriction condition
+
+ # Generate the restriction condition
$args->{full_field} = "classifications.name";
$self->_do_operator_function($args);
my $term = $args->{term};
my ($chart_id, $joins, $value, $operator) =
@$args{qw(chart_id joins value operator)};
my $dbh = Bugzilla->dbh;
-
+
my $table = "idle_$chart_id";
my $quoted = $dbh->quote(SqlifyDate($value));
-
+
my $ld_table = "comment_$table";
- my $act_table = "activity_$table";
+ my $act_table = "activity_$table";
my $comments_join = {
table => 'longdescs',
as => $ld_table,
to => 'who',
extra => ["$act_table.bug_when > $quoted"]
};
-
+
push(@$joins, $comments_join, $activity_join);
-
+
if ($operator =~ /greater/) {
$args->{term} =
"$ld_table.who IS NULL AND $act_table.who IS NULL";
my ($chart_id, $field, $operator, $value)
= @$args{qw(chart_id field operator value)};
my $dbh = Bugzilla->dbh;
-
+
# We want things like "cf_multi_select=two+words" to still be
# considered a search for two separate words, unless we're using
# anyexact. (_all_values would consider that to be one "word" with a
# space in it, because it's not in the Boolean Charts).
my @words = $operator eq 'anyexact' ? $self->_all_values($args)
: split(/[\s,]+/, $value);
-
+
my @terms;
foreach my $word (@words) {
next if $word eq '';
$args->{quoted} = $dbh->quote($word);
push(@terms, $self->_multiselect_term($args));
}
-
+
# The spacing in the joins helps make the resulting SQL more readable.
if ($operator =~ /^any/) {
$args->{term} = join("\n OR ", @terms);
my ($self, $args) = @_;
my ($field, $chart_id) = @$args{qw(field chart_id)};
my $dbh = Bugzilla->dbh;
-
+
if ($field eq 'keywords') {
$args->{full_field} = 'keyworddefs.name';
return "keywords INNER JOIN keyworddefs".
my ($self, $args) = @_;
my ($full_field, $quoted) = @$args{qw(full_field quoted)};
my $dbh = Bugzilla->dbh;
-
+
$args->{term} = $dbh->sql_position($quoted, $full_field) . " > 0";
}
my ($self, $args) = @_;
my ($full_field, $quoted) = @$args{qw(full_field quoted)};
my $dbh = Bugzilla->dbh;
-
+
# XXX This should probably be changed to just use LIKE
$args->{term} = $dbh->sql_iposition($quoted, $full_field) . " > 0";
}
my ($self, $args) = @_;
my ($full_field, $quoted) = @$args{qw(full_field quoted)};
my $dbh = Bugzilla->dbh;
-
+
# XXX This should probably be changed to just use NOT LIKE
$args->{term} = $dbh->sql_iposition($quoted, $full_field) . " = 0";
}
my ($self, $args) = @_;
my ($full_field, $quoted) = @$args{qw(full_field quoted)};
my $dbh = Bugzilla->dbh;
-
+
$args->{term} = $dbh->sql_regexp($full_field, $quoted);
}
my ($self, $args) = @_;
my ($full_field, $quoted) = @$args{qw(full_field quoted)};
my $dbh = Bugzilla->dbh;
-
+
$args->{term} = $dbh->sql_not_regexp($full_field, $quoted);
}
my ($self, $args) = @_;
my ($field, $full_field) = @$args{qw(field full_field)};
my $dbh = Bugzilla->dbh;
-
+
my @list = $self->_all_values($args, ',');
@list = map { $self->_quote_unless_numeric($args, $_) } @list;
-
+
if (@list) {
$args->{term} = $dbh->sql_in($full_field, \@list);
}
my $field_object = $self->_chart_fields->{$field}
|| ThrowCodeError("invalid_field_name", { field => $field });
-
+
# Asking when creation_ts changed is just asking when the bug was created.
if ($field_object->name eq 'creation_ts') {
$args->{operator} =
$operator eq 'changedbefore' ? 'lessthaneq' : 'greaterthaneq';
return $self->_do_operator_function($args);
}
-
+
my $sql_operator = ($operator =~ /before/) ? '<=' : '>=';
my $field_id = $field_object->id;
# Charts on changed* fields need to be field-specific. Otherwise,
my ($self, $args) = @_;
my ($chart_id, $joins, $field, $operator, $quoted) =
@$args{qw(chart_id joins field operator quoted)};
-
+
my $column = ($operator =~ /from/) ? 'removed' : 'added';
my $field_object = $self->_chart_fields->{$field}
|| ThrowCodeError("invalid_field_name", { field => $field });
my ($self, $args) = @_;
my ($chart_id, $joins, $field, $operator, $value) =
@$args{qw(chart_id joins field operator value)};
-
+
my $field_object = $self->_chart_fields->{$field}
|| ThrowCodeError("invalid_field_name", { field => $field });
my $field_id = $field_object->id;
elsif ($column =~ /map_(\w+?)s?\.(login_)?name/i) {
return $1;
}
-
- # If it doesn't match the regexps above, check to see if the old
+
+ # If it doesn't match the regexps above, check to see if the old
# SQL fragment matches the SQL of an existing column
foreach my $key (%{ $self->COLUMNS }) {
next unless exists $self->COLUMNS->{$key}->{name};
push(@$children, condition(@_));
return;
}
-
+
my ($child) = @_;
return if !defined $child;
$child->isa(__PACKAGE__) || $child->isa('Bugzilla::Search::Condition')
return $cache->{quicksearch_fields} if $cache->{quicksearch_fields};
# Get all the fields whose names don't contain periods. (Fields that
- # contain periods are always handled in MAPPINGS.)
- my @db_fields = grep { $_->name !~ /\./ }
+ # contain periods are always handled in MAPPINGS.)
+ my @db_fields = grep { $_->name !~ /\./ }
@{ Bugzilla->fields({ obsolete => 0 }) };
my %full_map = (%{ MAPPINGS() }, map { $_->name => $_->name } @db_fields);
# the mappings, and otherwise "stat" can't match "status".
#
# Also, don't allow searching the _accessible stuff via quicksearch
- # (both because it's unnecessary and because otherwise
+ # (both because it's unnecessary and because otherwise
# "reporter_accessible" and "reporter" both match "rep".
delete @full_map{qw(rep_platform bug_status bug_file_loc bug_group
bug_severity bug_status
}
# Check if we match exactly one custom field, ignoring the cf_ on the
- # custom fields (to allow people to type things like "build" for
+ # custom fields (to allow people to type things like "build" for
# "cf_build").
my %cfless;
foreach my $name (@field_names) {
sub _special_field_syntax {
my ($word, $negate) = @_;
return unless defined($word);
-
+
# P1-5 Syntax
if ($word =~ m/^P(\d+)(?:-(\d+))?$/i) {
my ($p_start, $p_end) = ($1, $2);
addChart('priority', 'anyexact', $prios, $negate);
return 1;
}
- return 0;
+ return 0;
}
sub _default_quicksearch_word {
my ($word, $negate) = @_;
return unless defined($word);
-
+
if (!grep { lc($word) eq $_ } PRODUCT_EXCEPTIONS and length($word) > 2) {
addChart('product', 'substring', $word, $negate);
}
-
+
if (!grep { lc($word) eq $_ } COMPONENT_EXCEPTIONS and length($word) > 2) {
addChart('component', 'substring', $word, $negate);
}
-
+
my @legal_keywords = map($_->name, Bugzilla::Keyword->get_all);
if (grep { lc($word) eq lc($_) } @legal_keywords) {
addChart('keywords', 'substring', $word, $negate);
}
-
+
addChart('alias', 'substring', $word, $negate);
addChart('short_desc', 'substring', $word, $negate);
addChart('status_whiteboard', 'substring', $word, $negate);
return $search if $search->user->id == $user->id;
if (!$search->shared_with_group
- or !$user->in_group($search->shared_with_group))
+ or !$user->in_group($search->shared_with_group))
{
ThrowUserError('missing_query', { name => $search->name,
sharer_id => $search->user->id });
my $lif = delete $params->{link_in_footer};
my $obj = $class->insert_create_data($params);
if ($lif) {
- $dbh->do('INSERT INTO namedqueries_link_in_footer
+ $dbh->do('INSERT INTO namedqueries_link_in_footer
(user_id, namedquery_id) VALUES (?,?)',
undef, $params->{userid}, $obj->id);
}
my ($self) = @_;
return $self->{edit_link} if defined $self->{edit_link};
my $cgi = new Bugzilla::CGI($self->url);
- if (!$cgi->param('query_type')
+ if (!$cgi->param('query_type')
|| !IsValidQueryType($cgi->param('query_type')))
{
$cgi->param('query_type', 'advanced');
($self->{used_in_whine}) = Bugzilla->dbh->selectrow_array(
'SELECT 1 FROM whine_events INNER JOIN whine_queries
ON whine_events.id = whine_queries.eventid
- WHERE whine_events.owner_userid = ? AND query_name = ?', undef,
+ WHERE whine_events.owner_userid = ? AND query_name = ?', undef,
$self->{userid}, $self->name) || 0;
return $self->{used_in_whine};
}
my $user_id = $user ? $user->id : Bugzilla->user->id;
my $link_in_footer = Bugzilla->dbh->selectrow_array(
'SELECT 1 FROM namedqueries_link_in_footer
- WHERE namedquery_id = ? AND user_id = ?',
+ WHERE namedquery_id = ? AND user_id = ?',
undef, $self->id, $user_id) || 0;
$self->{link_in_footer} = $link_in_footer if !$user;
return $link_in_footer;
my ($group_id) = Bugzilla->dbh->selectrow_array(
'SELECT group_id FROM namedquery_group_map WHERE namedquery_id = ?',
undef, $self->id);
- $self->{shared_with_group} = $group_id ? new Bugzilla::Group($group_id)
+ $self->{shared_with_group} = $group_id ? new Bugzilla::Group($group_id)
: undef;
return $self->{shared_with_group};
}
return failure "Found $mailer but cannot execute it"
unless -x $mailer;
-
+
local $SIG{'CHLD'} = 'DEFAULT';
my $pipe = gensym;
# This module implements a series - a set of data to be plotted on a chart.
#
-# This Series is in the database if and only if self->{'series_id'} is defined.
-# Note that the series being in the database does not mean that the fields of
-# this object are the same as the DB entries, as the object may have been
+# This Series is in the database if and only if self->{'series_id'} is defined.
+# Note that the series being in the database does not mean that the fields of
+# this object are the same as the DB entries, as the object may have been
# altered.
package Bugzilla::Series;
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
-
+
# Create a ref to an empty hash and bless it
my $self = {};
bless($self, $class);
my $arg_count = scalar(@_);
-
- # new() can return undef if you pass in a series_id and the user doesn't
+
+ # new() can return undef if you pass in a series_id and the user doesn't
# have sufficient permissions. If you create a new series in this way,
# you need to check for an undef return, and act appropriately.
my $retval = $self;
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
- detaint_natural($series_id)
+ detaint_natural($series_id)
|| ThrowCodeError("invalid_series_id", { 'series_id' => $series_id });
my $grouplist = $user->groups_as_string;
$self->{'series_id'} = $cgi->param('series_id') || undef;
if (defined($self->{'series_id'})) {
detaint_natural($self->{'series_id'})
- || ThrowCodeError("invalid_series_id",
+ || ThrowCodeError("invalid_series_id",
{ 'series_id' => $self->{'series_id'} });
}
-
+
$self->{'category'} = $cgi->param('category')
|| $cgi->param('newcategory')
|| ThrowUserError("missing_category");
"category", "subcategory", "name",
"frequency", "public", "query_format");
trick_taint($self->{'query'});
-
+
$self->{'public'} = $cgi->param('public') ? 1 : 0;
-
+
# Change 'admin' here and in series.html.tmpl, or remove the check
# completely, if you want to change who can make series public.
$self->{'public'} = 0 unless Bugzilla->user->in_group('admin');
my $subcategory_id = getCategoryID($self->{'subcategory'});
my $exists;
- if ($self->{'series_id'}) {
- $exists =
+ if ($self->{'series_id'}) {
+ $exists =
$dbh->selectrow_array("SELECT series_id FROM series
WHERE series_id = $self->{'series_id'}");
}
-
- # Is this already in the database?
+
+ # Is this already in the database?
if ($exists) {
# Update existing series
my $dbh = Bugzilla->dbh;
"name = ?, frequency = ?, is_public = ? " .
"WHERE series_id = ?", undef,
$category_id, $subcategory_id, $self->{'name'},
- $self->{'frequency'}, $self->{'public'},
+ $self->{'frequency'}, $self->{'public'},
$self->{'series_id'});
}
else {
# Insert the new series into the series table
$dbh->do("INSERT INTO series (creator, category, subcategory, " .
- "name, frequency, query, is_public) VALUES " .
+ "name, frequency, query, is_public) VALUES " .
"(?, ?, ?, ?, ?, ?, ?)", undef,
$self->{'creator_id'}, $category_id, $subcategory_id, $self->{'name'},
$self->{'frequency'}, $self->{'query'}, $self->{'public'});
$self->{'series_id'}
|| ThrowCodeError("missing_series_id", { 'series' => $self });
}
-
+
$dbh->bz_commit_transaction();
}
my $category_id = getCategoryID($self->{'category'});
my $subcategory_id = getCategoryID($self->{'subcategory'});
-
+
trick_taint($self->{'name'});
my $series_id = $dbh->selectrow_array("SELECT series_id " .
"FROM series WHERE category = $category_id " .
"AND subcategory = $subcategory_id AND name = " .
$dbh->quote($self->{'name'}));
-
+
return($series_id);
}
use strict;
use warnings;
-# This subclasses Bugzilla::Field::Choice instead of implementing
+# This subclasses Bugzilla::Field::Choice instead of implementing
# ChoiceInterface, because a bug status literally is a special type
# of Field::Choice, not just an object that happens to have the same
# methods.
BUG_STATE_OPEN
SPECIAL_STATUS_WORKFLOW_ACTIONS
- is_open_state
+ is_open_state
closed_bug_statuses
);
sub is_static {
my $self = shift;
if ($self->name eq 'UNCONFIRMED'
- || $self->name eq Bugzilla->params->{'duplicate_or_move_bug_status'})
+ || $self->name eq Bugzilla->params->{'duplicate_or_move_bug_status'})
{
return 1;
}
sub comment_required_on_change_from {
my ($self, $old_status) = @_;
my ($cond, $values) = $self->_status_condition($old_status);
-
+
my ($require_comment) = Bugzilla->dbh->selectrow_array(
"SELECT require_comment FROM status_workflow
WHERE $cond", undef, @$values);
use Bugzilla::Constants;
use Bugzilla::Hook;
use Bugzilla::Install::Requirements;
-use Bugzilla::Install::Util qw(install_string template_include_path
+use Bugzilla::Install::Util qw(install_string template_include_path
include_languages);
use Bugzilla::Keyword;
use Bugzilla::Util;
sub _include_path {
my $lang = shift || '';
my $cache = Bugzilla->request_cache;
- $cache->{"template_include_path_$lang"} ||=
+ $cache->{"template_include_path_$lang"} ||=
template_include_path({ language => $lang });
return $cache->{"template_include_path_$lang"};
}
if (ref($replace) eq 'CODE') {
$text =~ s/$match/($things[$count++] = $replace->({matches => [
$1, $2, $3, $4,
- $5, $6, $7, $8,
+ $5, $6, $7, $8,
$9, $10]}))
&& ("\x{FDD2}" . ($count-1) . "\x{FDD3}")/egx;
}
else {
- $text =~ s/$match/($things[$count++] = $replace)
+ $text =~ s/$match/($things[$count++] = $replace)
&& ("\x{FDD2}" . ($count-1) . "\x{FDD3}")/egx;
}
}
# Provide tooltips for full bug links (Bug 74355)
my $urlbase_re = '(' . join('|',
- map { qr/$_/ } grep($_, Bugzilla->params->{'urlbase'},
+ map { qr/$_/ } grep($_, Bugzilla->params->{'urlbase'},
Bugzilla->params->{'sslbase'})) . ')';
$text =~ s~\b(${urlbase_re}\Qshow_bug.cgi?id=\E([0-9]+)(\#c([0-9]+))?)\b
~($things[$count++] = $bug_link_func->($3, $1, { comment_num => $5, user => $user })) &&
my $template = Bugzilla->template_inner;
my $linkified;
- $template->process('bug/link.html.tmpl',
+ $template->process('bug/link.html.tmpl',
{ bug => $bug, link_text => $link_text, %$options }, \$linkified);
return $linkified;
}
# YUI dependency resolution
sub yui_resolve_deps {
my ($yui, $yui_deps) = @_;
-
+
my @yui_resolved;
foreach my $yui_name (@$yui) {
my $deps = $yui_deps->{$yui_name} || [];
# Allow keys to start with an underscore or a dot.
$Template::Stash::PRIVATE = undef;
-# Add "contains***" methods to list variables that search for one or more
-# items in a list and return boolean values representing whether or not
+# Add "contains***" methods to list variables that search for one or more
+# items in a list and return boolean values representing whether or not
# one/all/any item(s) were found.
$Template::Stash::LIST_OPS->{ contains } =
sub {
$Template::Stash::LIST_OPS->{ containsany } =
sub {
my ($list, $items) = @_;
- foreach my $item (@$items) {
+ foreach my $item (@$items) {
if (ref $item && $item->isa('Bugzilla::Object')) {
return 1 if grep($_->id == $item->id, @$list);
} else {
# Allow us to still get the scalar if we use the list operation ".0" on it,
# as we often do for defaults in query.cgi and other places.
-$Template::Stash::SCALAR_OPS->{ 0 } =
+$Template::Stash::SCALAR_OPS->{ 0 } =
sub {
return $_[0];
};
# Add a "truncate" method to the Template Toolkit's "scalar" object
# that truncates a string to a certain length.
-$Template::Stash::SCALAR_OPS->{ truncate } =
+$Template::Stash::SCALAR_OPS->{ truncate } =
sub {
my ($string, $length, $ellipsis) = @_;
return $string if !$length || length($string) <= $length;
my $config = {
# Colon-separated list of directories containing templates.
- INCLUDE_PATH => $opts{'include_path'}
+ INCLUDE_PATH => $opts{'include_path'}
|| _include_path($opts{'language'}),
# allow PERL/RAWPERL because doing so can boost performance
return $var;
},
- # Sadly, different to the above. See http://www.json.org/
+ # Sadly, different to the above. See http://www.json.org/
# for details.
json => sub {
my ($var) = @_;
$var =~ s/\t/\\t/g;
return $var;
},
-
+
# Converts data to base64
base64 => sub {
my ($data) = @_;
# Strips out control characters excepting whitespace
strip_control_chars => sub {
my ($data) = @_;
- # Only run for utf8 to avoid issues with other multibyte encodings
+ # Only run for utf8 to avoid issues with other multibyte encodings
# that may be reassigning meaning to ascii characters.
if (Bugzilla->params->{'utf8'}) {
$data =~ s/(?![\t\r\n])[[:cntrl:]]//g;
}
return $data;
},
-
+
# HTML collapses newlines in element attributes to a single space,
# so form elements which may have whitespace (ie comments) need
# to be encoded using 
if ($data < 1024) {
return "$data bytes";
- }
+ }
else {
my $u;
foreach $u ('GB', 'MB', 'KB') {
} else {
$output = $var;
}
-
+
$output =~ s/(.{75,75})/$1\n /g;
return $output;
}, 1],
# We force filtering of every variable in key security-critical
- # places; we have a none filter for people to use when they
+ # places; we have a none filter for people to use when they
# really, really don't want a variable to be changed.
none => sub { return $_[0]; } ,
},
'urlbase' => sub { return Bugzilla::Util::correct_urlbase(); },
# Allow templates to access docs url with users' preferred language
- 'docs_urlbase' => sub {
+ 'docs_urlbase' => sub {
my $language = Bugzilla->current_language;
my $docs_urlbase = Bugzilla->params->{'docs_urlbase'};
$docs_urlbase =~ s/\%lang\%/$language/;
: 'Bugzilla::Template::Context';
Bugzilla::Hook::process('template_before_create', { config => $config });
- my $template = $class->new($config)
+ my $template = $class->new($config)
|| die("Template creation failed: " . $class->error());
# BMO - hook for defining new vmethods, etc
# into data/deleteme/.
if (-e $cache_dir) {
my $deleteme = "$datadir/deleteme";
-
+
print STDERR "\n\n",
- install_string('template_removal_failed',
- { deleteme => $deleteme,
+ install_string('template_removal_failed',
+ { deleteme => $deleteme,
template_cache => $cache_dir }), "\n\n";
mkpath($deleteme);
my $random = generate_random_password();
}
# Under mod_perl, we look for templates using the absolute path of the
- # template directory, which causes Template Toolkit to look for their
+ # template directory, which causes Template Toolkit to look for their
# *compiled* versions using the full absolute path under the data/template
# directory. (Like data/template/var/www/html/bugzilla/.) To avoid
# re-compiling templates under mod_perl, we symlink to the
# Check if the directory exists, because if there are no extensions,
# there won't be an "data/template/extensions" directory to link to.
if (-d $target) {
- # We use abs2rel so that the symlink will look like
- # "../../../../template" which works, while just
+ # We use abs2rel so that the symlink will look like
+ # "../../../../template" which works, while just
# "data/template/template/" doesn't work.
my $relative_target = File::Spec->abs2rel($target, $container);
my $pre_process = $self->config->{PRE_PROCESS};
# Checking bz_in_process tells us that we were indeed called as part of a
- # Context::process, and not at some other point.
+ # Context::process, and not at some other point.
#
# Checking $name makes sure that we're processing a file, and not just a
# block, by checking that the name has a period in it. We don't allow
# We also make sure that we don't run, ever, during the PRE_PROCESS
# templates, because if somebody calls Throw*Error globally inside of
# template_before_process, that causes an infinite recursion into
- # the PRE_PROCESS templates (because Bugzilla, while inside
+ # the PRE_PROCESS templates (because Bugzilla, while inside
# global/intialize.none.tmpl, loads the template again to create the
# template object for Throw*Error).
#
use base qw(Template::Plugin);
use Bugzilla::Constants;
-use Bugzilla::Install::Util qw(template_include_path);
+use Bugzilla::Install::Util qw(template_include_path);
use Bugzilla::Util;
use Bugzilla::Error;
# from the disk.
my $cache = Bugzilla->request_cache->{template_plugin_hook_cache} ||= {};
my $lang = $context->{bz_language} || '';
- $cache->{"${lang}__$extension_template"}
+ $cache->{"${lang}__$extension_template"}
||= $self->_get_hooks($extension_template);
# process() accepts an arrayref of templates, so we just pass the whole
=back
-=item B<Returns>
+=item B<Returns>
Output from processing template extension.
my $too_soon = $dbh->selectrow_array(
'SELECT 1 FROM tokens
WHERE userid = ? AND tokentype = ?
- AND issuedate > '
+ AND issuedate > '
. $dbh->sql_date_math('NOW()', '-', 10, 'MINUTE'),
undef, ($user->id, 'password'));
$vars->{'timezone'} = $user->timezone;
my $message = "";
- $template->process("account/password/forgotten-password.txt.tmpl",
+ $template->process("account/password/forgotten-password.txt.tmpl",
$vars, \$message)
|| ThrowTemplateError($template->error());
}
sub GenerateUniqueToken {
- # Generates a unique random token. Uses generate_random_password
+ # Generates a unique random token. Uses generate_random_password
# for the tokens themselves and checks uniqueness by searching for
# the token in the "tokens" table. Gives up if it can't come up
# with a token after about one hundred tries.
}
}
-# Returns an email change token if the user has one.
+# Returns an email change token if the user has one.
sub HasEmailChangeToken {
my $userid = shift;
my $dbh = Bugzilla->dbh;
'showmybugslink' => 0,
'disabledtext' => '',
'disable_mail' => 0,
- 'is_enabled' => 1,
+ 'is_enabled' => 1,
};
use constant DB_TABLE => 'profiles';
# XXX Can update profiles_activity here as soon as it understands
# field names like login_name.
-
+
return $changes;
}
# Check the name if it's a new user, or if we're changing the name.
if (!ref($invocant) || $invocant->login ne $name) {
- is_available_username($name)
+ is_available_username($name)
|| ThrowUserError('account_exists', { email => $name });
}
sub _check_password {
my ($self, $pass) = @_;
- # If the password is '*', do not encrypt it or validate it further--we
- # are creating a user who should not be able to log in using DB
+ # If the password is '*', do not encrypt it or validate it further--we
+ # are creating a user who should not be able to log in using DB
# authentication.
return $pass if $pass eq '*';
sub _check_is_enabled {
my ($invocant, $is_enabled, undef, $params) = @_;
- # is_enabled is set automatically on creation depending on whether
+ # is_enabled is set automatically on creation depending on whether
# disabledtext is empty (enabled) or not empty (disabled).
# When updating the user, is_enabled is set by calling set_disabledtext().
# Any value passed into this validator is ignored.
return "" unless $self->id;
if (!defined $self->{identity}) {
- $self->{identity} =
+ $self->{identity} =
$self->name ? $self->name . " <" . $self->login. ">" : $self->login;
}
FROM namedqueries_link_in_footer lif
INNER JOIN namedquery_group_map ngm
ON ngm.namedquery_id = lif.namedquery_id
- WHERE lif.user_id = ?
+ WHERE lif.user_id = ?
AND lif.namedquery_id NOT IN ($query_id_string)
AND " . $self->groups_in_sql,
undef, $self->id);
sub recent_searches {
my $self = shift;
- $self->{recent_searches} ||=
+ $self->{recent_searches} ||=
Bugzilla::Search::Recent->match({ user_id => $self->id });
return $self->{recent_searches};
}
sub save_last_search {
my ($self, $params) = @_;
- my ($bug_ids, $order, $vars, $list_id) =
+ my ($bug_ids, $order, $vars, $list_id) =
@$params{qw(bugs order vars list_id)};
my $cgi = Bugzilla->cgi;
else {
# If we already have an existing search with a totally
# identical bug list, then don't create a new one. This
- # prevents people from writing over their whole
+ # prevents people from writing over their whole
# recent-search list by just refreshing a saved search
# (which doesn't have list_id in the header) over and over.
my $list_string = join(',', @$bug_ids);
my $existing_search = Bugzilla::Search::Recent->match({
user_id => $self->id, bug_list => $list_string });
-
+
if (!scalar(@$existing_search)) {
$search = Bugzilla::Search::Recent->create({
user_id => $self->id,
LEFT JOIN cc
ON cc.bug_id = bugs.bug_id
AND cc.who = $user_id
- LEFT JOIN bug_group_map
+ LEFT JOIN bug_group_map
ON bugs.bug_id = bug_group_map.bug_id
AND bug_group_map.group_id NOT IN ("
. $self->groups_as_string . ')
$sth->execute(@check_ids);
my $use_qa_contact = Bugzilla->params->{'useqacontact'};
while (my $row = $sth->fetchrow_arrayref) {
- my ($bug_id, $reporter, $owner, $qacontact, $reporter_access,
+ my ($bug_id, $reporter, $owner, $qacontact, $reporter_access,
$cclist_access, $isoncclist, $missinggroup) = @$row;
- $visible_cache->{$bug_id} ||=
+ $visible_cache->{$bug_id} ||=
((($reporter == $user_id) && $reporter_access)
|| ($use_qa_contact
&& $qacontact && ($qacontact == $user_id))
ThrowUserError('no_products');
}
- my $product = blessed($input) ? $input
+ my $product = blessed($input) ? $input
: new Bugzilla::Product({ name => $input });
my $can_enter =
$product && grep($_->name eq $product->name,
sub get_accessible_products {
my $self = shift;
-
+
# Map the objects into a hash using the ids as keys
my %products = map { $_->id => $_ }
@{$self->get_selectable_products},
@{$self->get_enterable_products};
-
+
return [ sort { $a->name cmp $b->name } values %products ];
}
my $dbh = Bugzilla->dbh;
my $sth;
-
+
if (Bugzilla->params->{'usevisibilitygroups'}) {
$sth = $dbh->prepare("SELECT DISTINCT grantor_id
FROM group_group_map
my $self = shift;
if (!scalar(@_)) {
- # If we're called without an argument, just return
+ # If we're called without an argument, just return
# whether or not we can bless at all.
return scalar(@{ $self->bless_groups }) ? 1 : 0;
}
my @field_names = grep(/$field_pattern/, keys %$data);
foreach my $field_name (@field_names) {
- $expanded_fields->{$field_name} =
+ $expanded_fields->{$field_name} =
{ type => $fields->{$field_pattern}->{'type'} };
-
- # The field is a requestee field; in order for its name
- # to show up correctly on the confirmation page, we need
+
+ # The field is a requestee field; in order for its name
+ # to show up correctly on the confirmation page, we need
# to find out the name of its flag type.
if ($field_name =~ /^requestee(_type)?-(\d+)$/) {
my $flag_type;
my ($bug, $relationship, $fieldDiffs, $comments, $dep_mail, $changer) = @_;
# Make a list of the events which have happened during this bug change,
- # from the point of view of this user.
- my %events;
+ # from the point of view of this user.
+ my %events;
foreach my $change (@$fieldDiffs) {
my $fieldName = $change->{field_name};
# A change to any of the above fields sets the corresponding event
{
$events{+EVT_ADDED_REMOVED} = 1;
}
-
+
if ($fieldName eq "cc") {
my $login = $self->login;
my $inold = ($change->{old} =~ /^(.*,\s*)?\Q$login\E(,.*)?$/);
elsif (defined($$comments[0])) {
$events{+EVT_COMMENT} = 1;
}
-
+
# Dependent changed bugmails must have an event to ensure the bugmail is
# emailed.
if ($dep_mail) {
}
my @event_list = keys %events;
-
+
my $wants_mail = $self->wants_mail(\@event_list, $relationship);
# The negative events are handled separately - they can't be incorporated
# into the first wants_mail call, because they are of the opposite sense.
- #
+ #
# We do them separately because if _any_ of them are set, we don't want
# the mail.
if ($wants_mail && $changer && ($self->id == $changer->id)) {
$wants_mail &= $self->wants_mail([EVT_CHANGED_BY_ME], $relationship);
- }
-
+ }
+
if ($wants_mail && $bug->bug_status eq 'UNCONFIRMED') {
$wants_mail &= $self->wants_mail([EVT_UNCONFIRMED], $relationship);
}
sub wants_mail {
my $self = shift;
my ($events, $relationship) = @_;
-
- # Don't send any mail, ever, if account is disabled
+
+ # Don't send any mail, ever, if account is disabled
# XXX Temporary Compatibility Change 1 of 2:
# This code is disabled for the moment to make the behaviour like the old
# system, which sent bugmail to disabled accounts.
# return 0 if $self->{'disabledtext'};
-
+
# No mail if there are no events
return 0 if !scalar(@$events);
foreach my $rel (keys %relationships) {
foreach my $event (POS_EVENTS, NEG_EVENTS) {
# These "exceptions" define the default email preferences.
- #
+ #
# We enable mail unless the change was made by the user, or it's
# just a CC list addition and the user is not the reporter.
next if ($event == EVT_CHANGED_BY_ME);
sub account_ip_login_failures {
my $self = shift;
my $dbh = Bugzilla->dbh;
- my $time = $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-',
+ my $time = $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-',
LOGIN_LOCKOUT_INTERVAL, 'MINUTE');
my $ip_addr = remote_ip();
trick_taint($ip_addr);
my $dbh = Bugzilla->dbh;
my $cache = Bugzilla->request_cache->{user_login_to_id} ||= {};
- # We cache lookups because this function showed up as taking up a
+ # We cache lookups because this function showed up as taking up a
# significant amount of time in profiles of xt/search.t. However,
# for users that don't exist, we re-do the check every time, because
# otherwise we break is_available_username.
# statement only, so it's safe to simply trick_taint.
trick_taint($login);
$user_id = $dbh->selectrow_array(
- "SELECT userid FROM profiles
+ "SELECT userid FROM profiles
WHERE " . $dbh->sql_istrcmp('login_name', '?'), undef, $login);
$cache->{$login} = $user_id;
}
my $user = new Bugzilla::User($id);
- my @get_selectable_classifications =
+ my @get_selectable_classifications =
$user->get_selectable_classifications;
# Class Functions
- $user = Bugzilla::User->create({
- login_name => $username,
- realname => $realname,
- cryptpassword => $plaintext_password,
+ $user = Bugzilla::User->create({
+ login_name => $username,
+ realname => $realname,
+ cryptpassword => $plaintext_password,
disabledtext => $disabledtext,
disable_mail => 0});
=item C<USER_MATCH_MULTIPLE>
-Returned by C<match_field()> when at least one field matched more than
+Returned by C<match_field()> when at least one field matched more than
one user, but no matches failed.
=item C<USER_MATCH_FAILED>
-Returned by C<match_field()> when at least one field failed to match
+Returned by C<match_field()> when at least one field failed to match
anything.
=item C<USER_MATCH_SUCCESS>
=item C<MATCH_SKIP_CONFIRM>
-Passed in to match_field to tell match_field to never display a
+Passed in to match_field to tell match_field to never display a
confirmation screen.
=back
=item C<super_user>
Returns a user who is in all groups, but who does not really exist in the
-database. Used for non-web scripts like L<checksetup> that need to make
+database. Used for non-web scripts like L<checksetup> that need to make
database changes and so on.
=back
=item C<queries>
-Returns an arrayref of the user's own saved queries, sorted by name. The
+Returns an arrayref of the user's own saved queries, sorted by name. The
array contains L<Bugzilla::Search::Saved> objects.
=item C<queries_subscribed>
for themselves or must accept the global site default value
default_value - the global site default for this setting
value - the value of this setting for this user. Will be the same
- as the default_value if the user is not logged in, or if
+ as the default_value if the user is not logged in, or if
is_default is true.
is_default - a boolean to indicate whether the user has chosen to make
a preference for themself or use the site default.
=item C<in_group_id>
-Determines whether or not a user is in the given group by id.
+Determines whether or not a user is in the given group by id.
=item C<bless_groups>
=item C<clear_product_cache>
-Clears the stored values for L</get_selectable_products>,
+Clears the stored values for L</get_selectable_products>,
L</get_enterable_products>, etc. so that their data will be read from
the database again. Used mostly by L<Bugzilla::Product>.
Returns a boolean indicating whether or not the supplied username is
already taken in Bugzilla.
-Params: $username (scalar, string) - The full login name of the username
+Params: $username (scalar, string) - The full login name of the username
that you are checking.
$old_username (scalar, string) - If you are checking an email-change
token, insert the "old" username that the user is changing from,
- here. Then, as long as it's the right user for that token, he
+ here. Then, as long as it's the right user for that token, he
can change his username to $username. (That is, this function
will return a boolean true value).
my $dbh = Bugzilla->dbh;
# Confirm that the $setting_name is properly formed;
- # if not, throw a code error.
- #
+ # if not, throw a code error.
+ #
# NOTE: due to the way that setting names are used in templates,
# they must conform to to the limitations set for HTML NAMEs and IDs.
#
ON setting.name = profile_setting.setting_name
WHERE name = ?
AND profile_setting.user_id = ?},
- undef,
+ undef,
$setting_name, $user_id);
# if not defined, then grab the default value
=head1 SYNOPSIS
Setting.pm creates a setting object, which is a hash containing the user
-preference information for a single preference for a single user. These
-are usually accessed through the "settings" object of a user, and not
+preference information for a single preference for a single user. These
+are usually accessed through the "settings" object of a user, and not
directly.
=head1 DESCRIPTION
=item C<add_setting($name, \@values, $default_value, $subclass, $force_check, $category)>
-Description: Checks for the existence of a setting, and adds it
+Description: Checks for the existence of a setting, and adds it
to the database if it does not yet exist.
Params: C<$name> - string - the name of the new setting
=item C<get_all_settings($user_id)>
-Description: Provides the user's choices for each setting in the
+Description: Provides the user's choices for each setting in the
system; if the user has made no choice, uses the site
default instead.
Params: C<$user_id> - integer - the user id.
=item C<reset_to_default>
-Description: If a user chooses to use the global default for a given
- setting, their saved entry is removed from the database via
+Description: If a user chooses to use the global default for a given
+ setting, their saved entry is removed from the database via
this subroutine.
Params: none
Returns: nothing
=item C<set($value)>
-Description: If a user chooses to use their own value rather than the
+Description: If a user chooses to use their own value rather than the
global value for a given setting, OR changes their value for
- a given setting, this subroutine is called to insert or
+ a given setting, this subroutine is called to insert or
update the database as appropriate.
Params: C<$value> - string - the new value for this setting for this user.
Returns: nothing
$var =~ s/>/\>/g;
$var =~ s/\"/\"/g;
$var =~ s/\'/\'/g;
-
+
# the following nukes characters disallowed by the XML 1.0
- # spec, Production 2.2. 1.0 declares that only the following
+ # spec, Production 2.2. 1.0 declares that only the following
# are valid:
# (#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF])
$var =~ s/([\x{0001}-\x{0008}]|
return if !Bugzilla->params->{'ssl_redirect'};
my $sslbase = Bugzilla->params->{'sslbase'};
-
+
# If we're already running under SSL, never redirect.
return if uc($ENV{HTTPS} || '') eq 'ON';
# Never redirect if there isn't an sslbase.
local $Text::Wrap::columns = $columns;
local $Text::Wrap::unexpand = 0;
local $Text::Wrap::huge = 'wrap';
-
+
my $wrapped = wrap('', '', $string);
chomp($wrapped);
return $wrapped;
# If $format is not set, try to guess the correct date format.
if (!$format) {
if (!ref $date
- && $date =~ /^(\d{4})[-\.](\d{2})[-\.](\d{2}) (\d{2}):(\d{2})(:(\d{2}))?$/)
+ && $date =~ /^(\d{4})[-\.](\d{2})[-\.](\d{2}) (\d{2}):(\d{2})(:(\d{2}))?$/)
{
my $sec = $7;
if (defined $sec) {
second => defined($time[0]) ? int($time[0]) : undef,
# If a timezone was specified, use it. Otherwise, use the
# local timezone.
- time_zone => Bugzilla->local_timezone->offset_as_string($time[6])
+ time_zone => Bugzilla->local_timezone->offset_as_string($time[6])
|| Bugzilla->local_timezone,
);
}
# We append the algorithm used to the string. This is good because then
- # we can change the algorithm being used, in the future, without
+ # we can change the algorithm being used, in the future, without
# disrupting the validation of existing passwords. Also, this tells
# us if a password is using the old "crypt" method of hashing passwords,
# because the algorithm will be missing from the string.
# tainted. See http://rt.perl.org/rt3/Public/Bug/Display.html?id=59998
unless(tainted($password) || tainted($salt)) {
untaint($crypted_password);
- }
+ }
}
else {
my $hasher = Digest->new($algorithm);
if ($ts) {
$date2 = time2str("%Y-%m-%d", $ts);
- $date =~ s/(\d+)-0*(\d+?)-0*(\d+?)/$1-$2-$3/;
+ $date =~ s/(\d+)-0*(\d+?)-0*(\d+?)/$1-$2-$3/;
$date2 =~ s/(\d+)-0*(\d+?)-0*(\d+?)/$1-$2-$3/;
}
my $ret = ($ts && $date eq $date2);
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',
+ 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) {
# Encode::Detect sometimes mis-detects various ISO encodings as iso-8859-8,
# but Encode::Guess can usually tell which one it is.
if ($encoding && $encoding eq 'iso-8859-8') {
- my $decoded_as = _guess_iso($data, 'iso-8859-8',
- # These are ordered this way because it gives the most
+ my $decoded_as = _guess_iso($data, 'iso-8859-8',
+ # These are ordered this way because it gives the most
# accurate results.
qw(iso-8859-7 iso-8859-2));
$encoding = $decoded_as if $decoded_as;
=item C<diff_arrays(\@old, \@new)>
- Description: Takes two arrayrefs, and will tell you what it takes to
+ Description: Takes two arrayrefs, and will tell you what it takes to
get from @old to @new.
Params: @old = array that you are changing from
@new = array that you are changing to
- Returns: A list of two arrayrefs. The first is a reference to an
+ Returns: A list of two arrayrefs. The first is a reference to an
array containing items that were removed from @old. The
second is a reference to an array containing items
- that were added to @old. If both returned arrays are
+ that were added to @old. If both returned arrays are
empty, @old and @new contain the same values.
=back
=item C<detect_encoding($str)>
Guesses what encoding a given data is encoded in, returning the canonical name
-of the detected encoding (which may be different from the MIME charset
+of the detected encoding (which may be different from the MIME charset
specification).
=item C<clean_text($str)>
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
-# This is the base class for $self in WebService method calls. For the
+# This is the base class for $self in WebService method calls. For the
# actual RPC server, see Bugzilla::WebService::Server and its subclasses.
package Bugzilla::WebService;
=item C<email>
-A string representing an email address. This value, when returned,
+A string representing an email address. This value, when returned,
may be filtered based on if the user is logged in or not. May be null.
=item C<dateTime>
=head1 STABLE, EXPERIMENTAL, and UNSTABLE
Methods are marked B<STABLE> if you can expect their parameters and
-return values not to change between versions of Bugzilla. You are
+return values not to change between versions of Bugzilla. You are
best off always using methods marked B<STABLE>. We may add parameters
and additional items to the return values, but your old code will
always continue to work with any new changes we make. If we ever break
a B<STABLE> interface, we'll post a big notice in the Release Notes,
and it will only happen during a major new release.
-Methods (or parts of methods) are marked B<EXPERIMENTAL> if
-we I<believe> they will be stable, but there's a slight chance that
+Methods (or parts of methods) are marked B<EXPERIMENTAL> if
+we I<believe> they will be stable, but there's a slight chance that
small parts will change in the future.
Certain parts of a method's description may be marked as B<UNSTABLE>,
Many WebService methods return an array of structs with various
fields in the structs. (For example, L<Bugzilla::WebService::Bug/get>
-returns a list of C<bugs> that have fields like C<id>, C<summary>,
+returns a list of C<bugs> that have fields like C<id>, C<summary>,
C<creation_time>, etc.)
These parameters allow you to limit what fields are present in
=over
-=item C<include_fields>
+=item C<include_fields>
C<array> An array of strings, representing the (case-sensitive) names of
fields in the return value. Only the fields specified in this hash will
# Add aliases here for old method name compatibility #
######################################################
-BEGIN {
+BEGIN {
# In 3.0, get was called get_bugs
*get_bugs = \&get;
# Before 3.4rc1, "history" was get_history.
{
$has_values = 1;
@values = @{ $self->_legal_field_values({ field => $field }) };
- }
+ }
if (grep($_ eq $field->name, PRODUCT_SPECIFIC_FIELDS)) {
$value_field = 'product';
next if $change_to->id == $status->id;
my %change_to_hash = (
name => $self->type('string', $change_to->name),
- comment_required => $self->type('boolean',
+ comment_required => $self->type('boolean',
$change_to->comment_required_on_change_from($status)),
);
push(@can_change_to, \%change_to_hash);
sort_key => $self->type('int' , $value->sortkey),
sortkey => $self->type('int' , $value->sortkey), # deprecated
visibility_values => [
- defined $vis_val ? $self->type('string', $vis_val->name)
+ defined $vis_val ? $self->type('string', $vis_val->name)
: ()
],
});
foreach my $bug_id (@$bug_ids) {
my $bug = Bugzilla::Bug->check($bug_id);
# We want the API to always return comments in the same order.
-
+
my $comments = $bug->comments({ order => 'oldest_to_newest',
after => $params->{new_since} });
my @result;
ThrowUserError('comment_id_invalid', { id => $comment_id });
}
}
-
+
# Now make sure that we can see all the associated bugs.
my %got_bug_ids = map { $_->bug_id => 1 } @$comment_data;
Bugzilla::Bug->check($_) foreach (keys %got_bug_ids);
Bugzilla->switch_to_shadow_db() unless Bugzilla->user->id;
my $ids = $params->{ids};
- (defined $ids && scalar @$ids)
+ (defined $ids && scalar @$ids)
|| ThrowCodeError('param_required', { param => 'ids' });
my (@bugs, @faults, @hashes);
return { bugs => \@hashes, faults => \@faults };
}
-# this is a function that gets bug activity for list of bug ids
+# this is a function that gets bug activity for list of bug ids
# it can be called as the following:
# $call = $rpc->call( 'Bug.history', { ids => [1,2] });
sub history {
delete $change->{fieldname};
push (@{$bug_history{changes}}, $change);
}
-
+
push (@history, \%bug_history);
}
$item{history} = \@history;
# alias is returned in case users passes a mixture of ids and aliases
- # then they get to know which bug activity relates to which value
+ # then they get to know which bug activity relates to which value
# they passed
if (Bugzilla->params->{'usebugaliases'}) {
$item{alias} = $self->type('string', $bug->alias);
# We skip certain fields because their set_ methods actually use
# the external names instead of the internal names.
- $params = Bugzilla::Bug::map_fields($params,
+ $params = Bugzilla::Bug::map_fields($params,
{ summary => 1, platform => 1, severity => 1, url => 1 });
my $ids = delete $params->{ids};
$change->[1] = '' if !defined $change->[1];
$hash{changes}->{$api_field} = {
removed => $self->type('string', $change->[0]),
- added => $self->type('string', $change->[1])
+ added => $self->type('string', $change->[1])
};
}
Bugzilla->switch_to_shadow_db();
- defined $params->{field}
+ defined $params->{field}
or ThrowCodeError('param_required', { param => 'field' });
- my $field = Bugzilla::Bug::FIELD_MAP->{$params->{field}}
+ my $field = Bugzilla::Bug::FIELD_MAP->{$params->{field}}
|| $params->{field};
my @global_selects =
$attachment->update($timestamp);
my $comment = $params->{comment} || '';
- $attachment->bug->add_comment($comment,
+ $attachment->bug->add_comment($comment,
{ isprivate => $attachment->isprivate,
type => CMT_ATTACHMENT_CREATED,
extra_data => $attachment->id });
foreach my $id (@{ $params->{ids} }) {
my $bug = Bugzilla::Bug->check($id);
$user->can_edit_product($bug->product_id)
- || ThrowUserError("product_edit_denied",
+ || ThrowUserError("product_edit_denied",
{ product => $bug->product });
push(@bugs, $bug);
if ($remove) {
$bug->add_see_also($_) foreach @$add;
}
}
-
+
my %changes;
foreach my $bug (@bugs) {
my $change = $bug->update();
or defined $params->{attachment_ids}))
{
ThrowCodeError('param_required',
- { function => 'Bug.attachments',
+ { function => 'Bug.attachments',
params => ['ids', 'attachment_ids'] });
}
id => $self->type('int', $flag->id),
name => $self->type('string', $flag->name),
type_id => $self->type('int', $flag->type_id),
- creation_date => $self->type('dateTime', $flag->creation_date),
- modification_date => $self->type('dateTime', $flag->modification_date),
+ creation_date => $self->type('dateTime', $flag->creation_date),
+ modification_date => $self->type('dateTime', $flag->modification_date),
status => $self->type('string', $flag->status)
};
Each hash has the following keys:
-=over
+=over
=item C<name>
=back
-=item B<Returns>
+=item B<Returns>
C<values> - An array of strings: the legal values for this field.
The values will be sorted as they normally would be in Bugzilla.
It allows you to get data about attachments, given a list of bugs
and/or attachment ids.
-B<Note>: Private attachments will only be returned if you are in the
+B<Note>: Private attachments will only be returned if you are in the
insidergroup or if you are the submitter of the attachment.
=item B<REST>
=over
-=item C<id>
+=item C<id>
C<int> The id of the flag.
=item B<Description>
-This allows you to get data about comments, given a list of bugs
+This allows you to get data about comments, given a list of bugs
and/or comment ids.
=item B<REST>
All of the comments (that are visible to you) will be returned for the
specified bugs.
-=item C<comment_ids>
+=item C<comment_ids>
C<array> An array of integer comment_ids. These comments will be
returned individually, separate from any other comments in their
Each individual comment requested in C<comment_ids> is returned here,
in a hash where the numeric comment id is the key, and the value
-is the comment. (The format of comments is described below.)
+is the comment. (The format of comments is described below.)
=back
An array of numbers and strings.
If an element in the array is entirely numeric, it represents a bug_id
-from the Bugzilla database to fetch. If it contains any non-numeric
-characters, it is considered to be a bug alias instead, and the bug with
-that alias will be loaded.
+from the Bugzilla database to fetch. If it contains any non-numeric
+characters, it is considered to be a bug alias instead, and the bug with
+that alias will be loaded.
Note that it's possible for aliases to be disabled in Bugzilla, in which
case you will be told that you have specified an invalid bug_id if you
C<boolean> Normally, if you request any inaccessible or invalid bug ids,
Bug.get will throw an error. If this parameter is True, instead of throwing an
-error we return an array of hashes with a C<id>, C<faultString> and C<faultCode>
-for each bug that fails, and return normal information for the other bugs that
+error we return an array of hashes with a C<id>, C<faultString> and C<faultCode>
+for each bug that fails, and return normal information for the other bugs that
were accessible.
=back
=item C<bugs>
-An array of hashes that contains information about the bugs with
+An array of hashes that contains information about the bugs with
the valid ids. Each hash contains the following items:
These fields are returned by default or by specifying C<_default>
=item C<dupe_of>
-C<int> The bug ID of the bug that this bug is a duplicate of. If this bug
+C<int> The bug ID of the bug that this bug is a duplicate of. If this bug
isn't a duplicate of any bug, this will be null.
=item C<estimated_time>
=over
-=item C<id>
+=item C<id>
C<int> The id of the flag.
=item Date/Time Fields - C<dateTime>
-=back
+=back
=item I<user detail hashes>
C<int> The numeric bug_id of this bug.
-=item faultString
+=item faultString
c<string> This will only be returned for invalid bugs if the C<permissive>
-argument was set when calling Bug.get, and it is an error indicating that
+argument was set when calling Bug.get, and it is an error indicating that
the bug id was invalid.
=item faultCode
c<int> This will only be returned for invalid bugs if the C<permissive>
-argument was set when calling Bug.get, and it is the error code for the
+argument was set when calling Bug.get, and it is the error code for the
invalid bug error.
=back
=over
-=item C<permissive> argument added to this method's params in Bugzilla B<3.4>.
+=item C<permissive> argument added to this method's params in Bugzilla B<3.4>.
=item The following properties were added to this method's return values
in Bugzilla B<3.4>:
=item assigned_to
-=item component
+=item component
=item dupe_of
=item status
-=back
+=back
=item C<faults>
-=back
+=back
=item In Bugzilla B<4.0>, the following items were added to the C<bugs>
return value: C<blocks>, C<cc>, C<classification>, C<creator>,
-C<deadline>, C<depends_on>, C<estimated_time>, C<is_cc_accessible>,
+C<deadline>, C<depends_on>, C<estimated_time>, C<is_cc_accessible>,
C<is_confirmed>, C<is_creator_accessible>, C<groups>, C<keywords>,
C<op_sys>, C<platform>, C<qa_contact>, C<remaining_time>, C<see_also>,
C<target_milestone>, C<update_token>, C<url>, C<version>, C<whiteboard>,
An array of numbers and strings.
-If an element in the array is entirely numeric, it represents a bug_id
-from the Bugzilla database to fetch. If it contains any non-numeric
-characters, it is considered to be a bug alias instead, and the data bug
+If an element in the array is entirely numeric, it represents a bug_id
+from the Bugzilla database to fetch. If it contains any non-numeric
+characters, it is considered to be a bug alias instead, and the data bug
with that alias will be loaded.
item C<new_since>
=item alias
-C<string> The alias of this bug. If there is no alias or aliases are
+C<string> The alias of this bug. If there is no alias or aliases are
disabled in this Bugzilla, this will be undef.
=item history
=item changes
C<array> An array of hashes which contain all the changes that happened
-to the bug at this time (as specified by C<when>). Each hash contains
+to the bug at this time (as specified by C<when>). Each hash contains
the following items:
=over
=item removed
-C<string> The previous value of the bug field which has been deleted
+C<string> The previous value of the bug field which has been deleted
by the change.
=item added
=item attachment_id
-C<int> The id of the attachment that was changed. This only appears if
+C<int> The id of the attachment that was changed. This only appears if
the change was to an attachment, otherwise C<attachment_id> will not be
present in this hash.
Note that you will only be returned information about bugs that you
can see. Bugs that you can't see will be entirely excluded from the
-results. So, if you want to see private bugs, you will have to first
+results. So, if you want to see private bugs, you will have to first
log in and I<then> call this method.
=item B<Errors>
=item 50 (Param Required)
-You must specify a value for C<summary> containing a string of keywords to
+You must specify a value for C<summary> containing a string of keywords to
search for duplicates.
=back
=item B<Params>
Unless otherwise specified in the description of a parameter, bugs are
-returned if they match I<exactly> the criteria you specify in these
+returned if they match I<exactly> the criteria you specify in these
parameters. That is, we don't match against substrings--if a bug is in
the "Widgets" product and you ask for bugs in the "Widg" product, you
won't get anything.
=item C<count_only>
-C<boolean> If count_only set to true, only a single hash key called C<bug_count>
+C<boolean> If count_only set to true, only a single hash key called C<bug_count>
will be returned which is the number of bugs that matched the search.
=item C<quicksearch>
Note that you will only be returned information about bugs that you
can see. Bugs that you can't see will be entirely excluded from the
-results. So, if you want to see private bugs, you will have to first
+results. So, if you want to see private bugs, you will have to first
log in and I<then> call this method.
=item B<Errors>
are marked B<Defaulted>.
Clients that want to be able to interact uniformly with multiple
-Bugzillas should always set both the params marked B<Required> and those
+Bugzillas should always set both the params marked B<Required> and those
marked B<Defaulted>, because some Bugzillas may not have defaults set
for B<Defaulted> parameters, and then this method will throw an error
if you don't specify them.
=item C<version> (string) B<Required> - A version of the product above;
the version the bug was found in.
-=item C<description> (string) B<Defaulted> - The initial description for
+=item C<description> (string) B<Defaulted> - The initial description for
this bug. Some Bugzilla installations require this to not be blank.
=item C<op_sys> (string) B<Defaulted> - The operating system the bug was
=item C<severity> (string) B<Defaulted> - How severe the bug is.
-=item C<alias> (string) - A brief alias for the bug that can be used
+=item C<alias> (string) - A brief alias for the bug that can be used
instead of a bug number when accessing this bug. Must be unique in
all of this Bugzilla.
=over
-=item C<id> (int or string) B<Required> - The id or alias of the bug to append a
+=item C<id> (int or string) B<Required> - The id or alias of the bug to append a
comment to.
=item C<comment> (string) B<Required> - The comment to append to the bug.
If this is empty or all whitespace, an error will be thrown saying that
you did not set the C<comment> parameter.
-=item C<is_private> (boolean) - If set to true, the comment is private,
+=item C<is_private> (boolean) - If set to true, the comment is private,
otherwise it is assumed to be public.
=item C<work_time> (double) - Adds this many hours to the "Hours Worked"
You specified a C<work_time> larger than the maximum allowed value of
C<99999.99>.
-=item 100 (Invalid Bug Alias)
+=item 100 (Invalid Bug Alias)
If you specified an alias and either: (a) the Bugzilla you're querying
doesn't support aliases or (b) there is no bug with that alias.
=item C<alias>
-(string) The alias of the bug. You can only set this if you are modifying
+(string) The alias of the bug. You can only set this if you are modifying
a single bug. If there is more than one bug specified in C<ids>, passing in
a value for C<alias> will cause an error to be thrown.
=item C<add> An array of C<strings>s. The names of keywords to add to
the field on the bug. Passing something that isn't a valid keyword name
-will cause an error to be thrown.
+will cause an error to be thrown.
=item C<remove> An array of C<string>s. The names of keywords to remove
from the field on the bug. Passing something that isn't a valid keyword
C<string> The name of the product that the bug is in. If you change
this, you will probably also want to change C<target_milestone>,
C<version>, and C<component>, since those have different legal
-values in every product.
+values in every product.
If you cannot change the C<target_milestone> field, it will be reset to
the default for the product, when you move a bug to a new product.
Here's an example of what a return value might look like:
- {
+ {
bugs => [
{
id => 123,
},
keywords => {
removed => 'bar',
- added => 'qux, quo, qui',
+ added => 'qux, quo, qui',
}
},
}
# comment that it was retired. Also, if an error changes its name, you'll
# have to fix it here.
use constant WS_ERROR_CODE => {
- # Generic errors (Bugzilla::Object and others) are 50-99.
+ # Generic errors (Bugzilla::Object and others) are 50-99.
object_not_specified => 50,
reassign_to_empty => 50,
param_required => 50,
password_too_short => 502,
# Error 503 password_too_long no longer exists.
invalid_username => 504,
- # This is from strict_isolation, but it also basically means
+ # This is from strict_isolation, but it also basically means
# "invalid user."
invalid_user_group => 504,
user_access_by_id_denied => 505,
# BugUserLastVisited errors
user_not_involved => 1300,
- # Errors thrown by the WebService itself. The ones that are negative
+ # Errors thrown by the WebService itself. The ones that are negative
# conform to http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
xmlrpc_invalid_value => -32600,
unknown_method => -32601,
eval "require $class";
ThrowCodeError('unknown_method', {method => $full_method}) if $@;
- return if ($class->login_exempt($method)
+ return if ($class->login_exempt($method)
and !defined Bugzilla->input_params->{Bugzilla_login});
Bugzilla->login();
my $params;
if (defined $cgi->param('params')) {
local $@;
- $params = eval {
- $self->json->decode(scalar $cgi->param('params'))
+ $params = eval {
+ $self->json->decode(scalar $cgi->param('params'))
};
if ($@) {
ThrowUserError('json_rpc_invalid_params',
sub type {
my ($self, $type, $value) = @_;
-
+
# This is the only type that does something special with undef.
if ($type eq 'boolean') {
return $value ? JSON::true : JSON::false;
}
-
+
return JSON::null if !defined $value;
my $retval = $value;
if (defined $params->{$field}) {
my $value = $params->{$field};
if (ref $value eq 'ARRAY') {
- $params->{$field} =
+ $params->{$field} =
[ map { $self->datetime_format_inbound($_) } @$value ];
}
else {
# methods that can change data. This protects us against cross-site
# request forgeries.
if (!grep($_ eq $method, $pkg->READ_ONLY)) {
- ThrowUserError('json_rpc_post_only',
+ ThrowUserError('json_rpc_post_only',
{ method => $self->_bz_method_name });
}
}
$self->handle_login();
# Bugzilla::WebService packages call internal methods like
- # $self->_some_private_method. So we have to inherit from
+ # $self->_some_private_method. So we have to inherit from
# that class as well as this Server class.
my $new_class = ref($self) . '::' . $pkg;
my $isa_string = 'our @ISA = qw(' . ref($self) . " $pkg)";
# _bz_method_name is stored by _find_procedure for later use.
sub _bz_method_name {
- return $_[0]->{_bz_method_name};
+ return $_[0]->{_bz_method_name};
}
sub _bz_callback {
To connect over GET, simply send the values that you'd normally send for
each JSON-RPC argument as URL parameters, with the C<params> item being
-a JSON string.
+a JSON string.
The simplest example is a call to C<Bugzilla.time>:
To use JSONP with Bugzilla's JSON-RPC WebService, simply specify a
C<callback> parameter to jsonrpc.cgi when using it via GET as described above.
-For example, here's some HTML you could use to get the data from
+For example, here's some HTML you could use to get the data from
C<Bugzilla.time> on a remote website, using JSONP:
- <script type="text/javascript"
+ <script type="text/javascript"
src="http://bugzilla.example.com/jsonrpc.cgi?method=Bugzilla.time&callback=foo">
That would call the C<Bugzilla.time> method and pass its value to a function
So, for example, in JSON-RPC 1.0, an error response would look like:
- {
- result: null,
- error: { message: 'Some message here', code: 123 },
+ {
+ result: null,
+ error: { message: 'Some message here', code: 123 },
id: 1
}
=head1 NAME
-Bugzilla::Webservice::Server::REST::Resources::Group - The REST API for
+Bugzilla::Webservice::Server::REST::Resources::Group - The REST API for
creating, changing, and getting information about Groups.
=head1 DESCRIPTION
my $self = shift;
my ($type) = @{ $_[0] };
my $value = $self->SUPER::decode_value(@_);
-
+
# We only validate/convert certain types here.
return $value if $type !~ /^(?:int|i4|boolean|double|dateTime\.iso8601)$/;
-
+
# Though the XML-RPC standard doesn't allow an empty <int>,
# <double>,or <dateTime.iso8601>, we do, and we just say
# "that's undef".
if (grep($type eq $_, qw(int double dateTime))) {
return undef if $value eq '';
}
-
+
my $validator = $self->_validation_subs->{$type};
if (!$validator->($value)) {
ThrowUserError('xmlrpc_invalid_value',
{ type => $type, value => $value });
}
-
+
# We convert dateTimes to a DB-friendly date format.
if ($type eq 'dateTime.iso8601') {
if ($value !~ /T.*[\-+Z]/i) {
# The only place that XMLRPC::Lite stores any sort of validation
# regex is in XMLRPC::Serializer. We want to re-use those regexes here.
my $lookup = Bugzilla::XMLRPC::Serializer->new->typelookup;
-
+
# $lookup is a hash whose values are arrayrefs, and whose keys are the
# names of types. The second item of each arrayref is a subroutine
# that will do our validation for us.
# XMLRPC::Serializer than their standard XML-RPC name.
$validators{'dateTime.iso8601'} = $validators{'dateTime'};
$validators{'i4'} = $validators{'int'};
-
+
$self->{_validation_subs} = \%validators;
return \%validators;
}
sub paramsin {
my $self = shift;
if (!$self->{bz_params_in}) {
- my @params = $self->SUPER::paramsin(@_);
+ my @params = $self->SUPER::paramsin(@_);
if ($self->{_bz_do_taint}) {
taint_data(@params);
}
return as_nil();
}
else {
- my $super_method = "SUPER::$method";
+ my $super_method = "SUPER::$method";
return $self->$super_method($value);
}
}
Bugzilla does not use C<< <nil> >> values in returned data, because currently
most clients do not support C<< <nil> >>. Instead, any fields with C<undef>
values will be stripped from the response completely. Therefore
-B<the client must handle the fact that some expected fields may not be
+B<the client must handle the fact that some expected fields may not be
returned>.
=begin private
return $self->_login_to_hash($user);
}
- # Username and password params are required
+ # Username and password params are required
foreach my $param ("login", "password") {
(defined $params->{$param} || defined $params->{'Bugzilla_' . $param})
|| ThrowCodeError('param_required', { param => $param });
my $self = shift;
my ($params) = @_;
- Bugzilla->user->in_group('editusers')
+ Bugzilla->user->in_group('editusers')
|| ThrowUserError("auth_failure", { group => "editusers",
action => "add",
object => "users"});
}
-# function to return user information by passing either user ids or
+# function to return user information by passing either user ids or
# login names or both together:
-# $call = $rpc->call( 'User.get', { ids => [1,2,3],
+# $call = $rpc->call( 'User.get', { ids => [1,2,3],
# names => ['testusera@redhat.com', 'testuserb@redhat.com'] });
sub get {
my ($self, $params) = validate(@_, 'names', 'ids', 'match', 'group_ids', 'groups');
defined($params->{names}) || defined($params->{ids})
|| defined($params->{match})
- || ThrowCodeError('params_required',
+ || ThrowCodeError('params_required',
{ function => 'User.get', params => ['ids', 'names', 'match'] });
my @user_objects;
# start filtering to remove duplicate user ids
my %unique_users = map { $_->id => $_ } @user_objects;
@user_objects = values %unique_users;
-
+
my @users;
# If the user is not logged in: Return an error if they passed any user ids.
my $obj_by_ids;
$obj_by_ids = Bugzilla::User->new_from_list($params->{ids}) if $params->{ids};
- # obj_by_ids are only visible to the user if he can see
+ # obj_by_ids are only visible to the user if he can see
# the otheruser, for non visible otheruser throw an error
foreach my $obj (@$obj_by_ids) {
if (Bugzilla->user->can_see_user($obj)){
push(@users, $user_info);
}
- Bugzilla::Hook::process('webservice_user_get',
+ Bugzilla::Hook::process('webservice_user_get',
{ webservice => $self, params => $params, users => \@users });
return { users => \@users };
my $user = Bugzilla->user;
- my @groups = map { Bugzilla::Group->check({ id => $_ }) }
+ my @groups = map { Bugzilla::Group->check({ id => $_ }) }
@{ $group_ids || [] };
if ($group_names) {
sub _group_to_hash {
my ($self, $group) = @_;
my $item = {
- id => $self->type('int', $group->id),
- name => $self->type('string', $group->name),
- description => $self->type('string', $group->description),
+ id => $self->type('int', $group->id),
+ name => $self->type('string', $group->name),
+ description => $self->type('string', $group->description),
};
return $item;
}
B<Note>: At least one of C<ids>, C<names>, or C<match> must be specified.
-B<Note>: Users will not be returned more than once, so even if a user
+B<Note>: Users will not be returned more than once, so even if a user
is matched by more than one argument, only one user will be returned.
In addition to the parameters below, this method also accepts the
=over
-=item C<ids> (array)
+=item C<ids> (array)
An array of integers, representing user ids.
=back
-=item B<Returns>
+=item B<Returns>
A hash containing one item, C<users>, that is an array of
hashes. Each hash describes a user, and has the following items:
=item id
-C<int> The unique integer ID that Bugzilla uses to represent this user.
+C<int> The unique integer ID that Bugzilla uses to represent this user.
Even if the user's login name changes, this will not change.
=item real_name
=item name
-C<string> The login name of the user. Note that in some situations this is
+C<string> The login name of the user. Note that in some situations this is
different than their email.
=item can_login
-C<boolean> A boolean value to indicate if the user can login into bugzilla.
+C<boolean> A boolean value to indicate if the user can login into bugzilla.
=item email_enabled
=item login_denied_text
C<string> A text field that holds the reason for disabling a user from logging
-into bugzilla, if empty then the user account is enabled. Otherwise it is
+into bugzilla, if empty then the user account is enabled. Otherwise it is
disabled/closed.
=item groups
=back
-B<Note>: The elements of the returned array (i.e. hashes) are ordered by the
+B<Note>: The elements of the returned array (i.e. hashes) are ordered by the
name of each saved search.
=back
B<Note>: If you are not logged in to Bugzilla when you call this function, you
will only be returned the C<id>, C<name>, and C<real_name> items. If you are
-logged in and not in editusers group, you will only be returned the C<id>, C<name>,
+logged in and not in editusers group, you will only be returned the C<id>, C<name>,
C<real_name>, C<email>, and C<can_login> items. The groups returned are filtered
based on your permission to bless each group.
=item 505 (User Access By Id or User-Matching Denied)
-Logged-out users cannot use the "ids" or "match" arguments to this
+Logged-out users cannot use the "ids" or "match" arguments to this
function.
=item 804 (Invalid Group Name)
=item C<group_ids> and C<groups> were added in Bugzilla B<4.0>.
-=item C<include_disabled> added in Bugzilla B<4.0>. Default behavior
+=item C<include_disabled> added in Bugzilla B<4.0>. Default behavior
for C<match> has changed to only returning enabled accounts.
=item C<groups> Added in Bugzilla B<4.4>.
foreach my $key (keys %$item) {
# Making something a hash key always untaints it, in Perl.
# However, we need to validate our argument names in some way.
- # We know that all hash keys passed in to the WebService will
+ # We know that all hash keys passed in to the WebService will
# match \w+, so we delete any key that doesn't match that.
if ($key !~ /^[\w\.\-]+$/) {
delete $item->{$key};
# sent any parameters at all, and we're getting @keys where
# $params should be.
return ($self, undef) if (defined $params and !ref $params);
-
- # If @keys is not empty then we convert any named
+
+ # If @keys is not empty then we convert any named
# parameters that have scalar values to arrayrefs
# that match.
foreach my $key (@keys) {
if (exists $params->{$key}) {
- $params->{$key} = ref $params->{$key}
- ? $params->{$key}
+ $params->{$key} = ref $params->{$key}
+ ? $params->{$key}
: [ $params->{$key} ];
}
}
=head1 SYNOPSIS
- filter({ include_fields => ['id', 'name'],
+ filter({ include_fields => ['id', 'name'],
exclude_fields => ['name'] }, $hash);
my $wants = filter_wants $params, 'field_name';
validate(@_, 'ids');
=head1 DESCRIPTION
This module exists to represent a query for a L<Bugzilla::Whine::Event>.
-Each event, which are groups of schedules and queries based on how the
+Each event, which are groups of schedules and queries based on how the
user configured the event, may have zero or more queries associated
with it. Additionally, the queries are selected from the user's saved
searches, or L<Bugzilla::Search::Saved> object with a matching C<name>
-attribute for the user.
+attribute for the user.
This is an implementation of L<Bugzilla::Object>, and so has all the
same methods available as L<Bugzilla::Object>, in addition to what is
);
use constant UPDATE_COLUMNS => qw(
- eventid
- run_day
- run_time
- run_next
- mailto
+ eventid
+ run_day
+ run_time
+ run_next
+ mailto
mailto_type
);
use constant NAME_FIELD => 'id';
return $self->{mailto_object};
}
-sub mailto_users {
+sub mailto_users {
my $self = shift;
return $self->{mailto_users} if exists $self->{mailto_users};
my $object = $self->mailto;
=item C<mailto>
-This is either a L<Bugzilla::User> or L<Bugzilla::Group> object to represent
-the user or group this scheduled event is set to be mailed to.
+This is either a L<Bugzilla::User> or L<Bugzilla::Group> object to represent
+the user or group this scheduled event is set to be mailed to.
=item C<mailto_users>
use Bugzilla::BugMail;
use Bugzilla::Constants;
use Bugzilla::Error;
-use Bugzilla::Flag;
-use Bugzilla::FlagType;
+use Bugzilla::Flag;
+use Bugzilla::FlagType;
use Bugzilla::User;
use Bugzilla::Util;
use Bugzilla::Bug;
{
diff();
}
-elsif ($action eq "viewall")
-{
- viewall();
+elsif ($action eq "viewall")
+{
+ viewall();
}
-elsif ($action eq "enter")
-{
+elsif ($action eq "enter")
+{
Bugzilla->login(LOGIN_REQUIRED);
- enter();
+ enter();
}
elsif ($action eq "insert")
{
Bugzilla->login(LOGIN_REQUIRED);
insert();
}
-elsif ($action eq "edit")
-{
- edit();
+elsif ($action eq "edit")
+{
+ edit();
}
-elsif ($action eq "update")
-{
+elsif ($action eq "update")
+{
Bugzilla->login(LOGIN_REQUIRED);
update();
}
elsif ($action eq "delete") {
delete_attachment();
}
-else
-{
+else
+{
ThrowUserError('unknown_action', {action => $action});
}
ThrowTemplateError($template->error());
exit;
}
-
+
my $attach_id = $cgi->param($param);
# Validate the specified attachment id. detaint kills $attach_id if
detaint_natural($attach_id)
|| ThrowUserError("invalid_attach_id",
{ attach_id => scalar $cgi->param($param) });
-
+
# Make sure the attachment exists in the database.
my $attachment = new Bugzilla::Attachment({ id => $attach_id, cache => 1 })
|| ThrowUserError("invalid_attach_id", { attach_id => $attach_id });
# Make sure the user is authorized to access this attachment's bug.
Bugzilla::Bug->check({ id => $attachment->bug_id, cache => 1 });
- if ($attachment->isprivate && $user->id != $attachment->attacher->id
- && !$user->is_insider)
+ if ($attachment->isprivate && $user->id != $attachment->attacher->id
+ && !$user->is_insider)
{
ThrowUserError('auth_failure', {action => 'access',
object => 'attachment',
# alternate host.
Bugzilla->login();
my $attachbase = Bugzilla->params->{'attachment_base'};
- # Replace %bugid% by the ID of the bug the attachment
+ # Replace %bugid% by the ID of the bug the attachment
# belongs to, if present.
$attachbase =~ s/\%bugid\%/$bug_id/;
# To avoid leaking information we redirect using the attachment ID only
my $flag_types = Bugzilla::FlagType::match({'target_type' => 'attachment',
'product_id' => $bug->product_id,
- 'component_id' => $bug->component_id,
+ 'component_id' => $bug->component_id,
'is_active' => 1});
$vars->{'flag_types'} = $flag_types;
$vars->{'any_flags_requesteeble'} =
($bug_status) = grep {$_->name eq $bug_status} @{$bug->status->can_change_to};
if ($bug_status && $bug_status->is_open
- && ($bug_status->name ne 'UNCONFIRMED'
+ && ($bug_status->name ne 'UNCONFIRMED'
|| $bug->product_obj->allows_unconfirmed))
{
$bug->set_bug_status($bug_status->name);
$vars->{'attachment'} = $attachment;
$vars->{'bugs'} = [$bug];
$vars->{'header_done'} = 1;
- $vars->{'sent_bugmail'} =
+ $vars->{'sent_bugmail'} =
Bugzilla::BugMail::Send($bug->id, { 'changer' => $user });
# BMO: add show_bug_format hook for experimental UI work
}
# If configured to not allow empty words, reject empty searches from the
-# Find a Specific Bug search form, including words being a single or
+# Find a Specific Bug search form, including words being a single or
# several consecutive whitespaces only.
if (!Bugzilla->params->{'search_allow_no_criteria'}
&& defined($cgi->param('content')) && $cgi->param('content') =~ /^\s*$/)
# the bug list as HTML and they have not disabled it by adding &serverpush=0
# to the URL.
#
-# Server push is a Netscape 3+ hack incompatible with MSIE, Lynx, and others.
+# Server push is a Netscape 3+ hack incompatible with MSIE, Lynx, and others.
# Even Communicator 4.51 has bugs with it, especially during page reload.
# http://www.browsercaps.org used as source of compatible browsers.
# Safari (WebKit) does not support it, despite a UA that says otherwise (bug 188712)
#
my $serverpush =
$format->{'extension'} eq "html"
- && exists $ENV{'HTTP_USER_AGENT'}
- && $ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/
+ && exists $ENV{'HTTP_USER_AGENT'}
+ && $ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/
&& (($ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/) || ($ENV{'HTTP_USER_AGENT'} =~ /MSIE 5.*Mac_PowerPC/))
&& $ENV{'HTTP_USER_AGENT'} !~ /(?:WebKit|Trident|KHTML)/
&& !$agent
# will throw a UserError. Leading and trailing whitespace
# will be stripped from this value before it is inserted
# into the DB.
-# query - The query part of the buglist.cgi URL, unencoded. Must not be
+# query - The query part of the buglist.cgi URL, unencoded. Must not be
# empty, or we will throw a UserError.
-# link_in_footer (optional) - 1 if the Named Query should be
+# link_in_footer (optional) - 1 if the Named Query should be
# displayed in the user's footer, 0 otherwise.
#
# All parameters are validated before passing them into the database.
#
-# Returns: A boolean true value if the query existed in the database
+# Returns: A boolean true value if the query existed in the database
# before, and we updated it. A boolean false value otherwise.
sub InsertNamedQuery {
my ($query_name, $query, $link_in_footer) = @_;
sub LookupSeries {
my ($series_id) = @_;
detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
-
+
my $dbh = Bugzilla->dbh;
my $result = $dbh->selectrow_array("SELECT query FROM series " .
"WHERE series_id = ?"
my $count = $dbh->selectrow_array("SELECT COUNT(quip)"
. " FROM quips WHERE approved = 1");
my $random = int(rand($count));
- my $quip =
- $dbh->selectrow_array("SELECT quip FROM quips WHERE approved = 1 " .
+ my $quip =
+ $dbh->selectrow_array("SELECT quip FROM quips WHERE approved = 1 " .
$dbh->sql_limit(1, $random));
return $quip;
}
# Backwards-compatibility - the old interface had cmdtype="runnamed" to run
# a named command, and we can't break this because it's in bookmarks.
-if ($cmdtype eq "runnamed") {
+if ($cmdtype eq "runnamed") {
$cmdtype = "dorem";
$remaction = "run";
}
# Now we're going to be running, so ensure that the params object is set up,
-# using ||= so that we only do so if someone hasn't overridden this
+# using ||= so that we only do so if someone hasn't overridden this
# earlier, for example by setting up a named query search.
# This will be modified, so make a copy.
# Generate a reasonable filename for the user agent to suggest to the user
# when the user saves the bug list. Uses the name of the remembered query
-# if available. We have to do this now, even though we return HTTP headers
-# at the end, because the fact that there is a remembered query gets
+# if available. We have to do this now, even though we return HTTP headers
+# at the end, because the fact that there is a remembered query gets
# forgotten in the process of retrieving it.
my $disp_prefix = "bugs";
if (($cmdtype eq "dorem" && $remaction =~ /^run/) || ($format->{extension} ne 'html' && defined $cgi->param('namedcmd'))) {
}
# Take appropriate action based on user's request.
-if ($cmdtype eq "dorem") {
+if ($cmdtype eq "dorem") {
if ($remaction eq "run") {
my $query_id;
($buffer, $query_id, $sharer_id) =
elsif ($remaction eq "forget") {
$user = Bugzilla->login(LOGIN_REQUIRED);
# Copy the name into a variable, so that we can trick_taint it for
- # the DB. We know it's safe, because we're using placeholders in
+ # the DB. We know it's safe, because we're using placeholders in
# the SQL, and the SQL is only a DELETE.
my $qname = $cgi->param('namedcmd');
trick_taint($qname);
# Do not forget the saved search if it is being used in a whine
- my $whines_in_use =
+ my $whines_in_use =
$dbh->selectcol_arrayref('SELECT DISTINCT whine_events.subject
FROM whine_events
INNER JOIN whine_queries
= ?
', undef, $user->id, $qname);
if (scalar(@$whines_in_use)) {
- ThrowUserError('saved_search_used_by_whines',
+ ThrowUserError('saved_search_used_by_whines',
{ subjects => join(',', @$whines_in_use),
search_name => $qname }
);
# Display Column Determination
################################################################################
-# Determine the columns that will be displayed in the bug list via the
+# Determine the columns that will be displayed in the bug list via the
# columnlist CGI parameter, the user's preferences, or the default.
my @displaycolumns = ();
if (defined $params->param('columnlist')) {
@displaycolumns = DEFAULT_COLUMN_LIST;
}
-# Weed out columns that don't actually exist to prevent the user
-# from hacking their column list cookie to grab data to which they
+# Weed out columns that don't actually exist to prevent the user
+# from hacking their column list cookie to grab data to which they
# should not have access. Detaint the data along the way.
@displaycolumns = grep($columns->{$_} && trick_taint($_), @displaycolumns);
push (@selectcolumns, $col) if !grep($_ eq $col, @selectcolumns);
}
-# If the user is editing multiple bugs, we also make sure to select the
+# If the user is editing multiple bugs, we also make sure to select the
# status, because the values of that field determines what options the user
# has for modifying the bugs.
if ($dotweak) {
if (!$order || $order =~ /^reuse/i) {
if ($cgi->cookie('LASTORDER')) {
$order = $cgi->cookie('LASTORDER');
-
+
# Cookies from early versions of Specific Search included this text,
# which is now invalid.
$order =~ s/ LIMIT 200//;
'time_present' => ($estimated_time || $remaining_time ||
$actual_time || $percentage_complete),
};
-
+
my $bugowners = {};
my $bugproducts = {};
my $bugstatuses = {};
# Process certain values further (i.e. date format conversion).
if ($bug->{'changeddate'}) {
- $bug->{'changeddate'} =~
+ $bug->{'changeddate'} =~
s/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/$1-$2-$3 $4:$5:$6/;
$bug->{'changedtime'} = $bug->{'changeddate'}; # for iCalendar and Atom
"LEFT JOIN group_control_map " .
"ON group_control_map.product_id = bugs.product_id " .
"AND group_control_map.group_id = bug_group_map.group_id " .
- "WHERE " . $dbh->sql_in('bugs.bug_id', \@bugidlist) .
+ "WHERE " . $dbh->sql_in('bugs.bug_id', \@bugidlist) .
$dbh->sql_group_by('bugs.bug_id'));
$sth->execute();
while (my ($bug_id, $min_membercontrol) = $sth->fetchrow_array()) {
if ($sum > 0) {
$time_info->{'percentage_complete'} = 100*$time_info->{'actual_time'}/$sum;
}
-else { # remaining_time <= 0
+else { # remaining_time <= 0
$time_info->{'percentage_complete'} = 0
-}
+}
################################################################################
# Template Variable Definition
$one_product = new Bugzilla::Product({ name => $product_input[0] });
}
}
-# We only want the template to use it if the user can actually
+# We only want the template to use it if the user can actually
# enter bugs against it.
if ($one_product && $user->can_enter_product($one_product)) {
$vars->{'one_product'} = $one_product;
object => 'multiple_bugs'});
}
$vars->{'dotweak'} = 1;
-
+
# issue_session_token needs to write to the master DB.
Bugzilla->switch_to_main_db();
$vars->{'token'} = issue_session_token('buglist_mass_change');
INNER JOIN bug_status
ON bug_status.id = sw1.new_status
WHERE bug_status.isactive = 1
- AND NOT EXISTS
+ AND NOT EXISTS
(SELECT * FROM status_workflow sw2
- WHERE sw2.old_status != sw1.new_status
+ WHERE sw2.old_status != sw1.new_status
AND '
. $dbh->sql_in('sw2.old_status', $bug_status_ids)
- . ' AND NOT EXISTS
+ . ' AND NOT EXISTS
(SELECT * FROM status_workflow sw3
WHERE sw3.new_status = sw1.new_status
AND sw3.old_status = sw2.old_status))');
$vars->{'versions'} = [map($_->name, grep($_->is_active, @{ $one_product->versions }))];
$vars->{'components'} = [map($_->name, grep($_->is_active, @{ $one_product->components }))];
if (Bugzilla->params->{'usetargetmilestone'}) {
- $vars->{'targetmilestones'} = [map($_->name, grep($_->is_active,
+ $vars->{'targetmilestones'} = [map($_->name, grep($_->is_active,
@{ $one_product->milestones }))];
}
}
# the "Remember search as" field.
$vars->{'defaultsavename'} = $cgi->param('query_based_on');
-# If we did a quick search then redisplay the previously entered search
+# If we did a quick search then redisplay the previously entered search
# string in the text field.
$vars->{'quicksearch'} = $searchstring;
assertCanCreate($cgi);
my $token = $cgi->param('token');
check_hash_token($token, ['create-series']);
-
+
my $series = new Bugzilla::Series($cgi);
ThrowUserError("series_already_exists", {'series' => $series})
# the return value is us or some other series we need to avoid stomping
# on.
my $id_of_series_in_db = $series->existsInDatabase();
- if (defined($id_of_series_in_db) &&
- $id_of_series_in_db != $series->{'series_id'})
+ if (defined($id_of_series_in_db) &&
+ $id_of_series_in_db != $series->{'series_id'})
{
ThrowUserError("series_already_exists", {'series' => $series});
}
-
+
$series->writeToDatabase();
$vars->{'changes_saved'} = 1;
-
+
edit($series);
}
elsif ($action eq "confirm-delete") {
return @ids;
}
-# Check if the user is the owner of series_id or is an admin.
+# Check if the user is the owner of series_id or is an admin.
sub assertCanEdit {
my $series_id = shift;
my $user = Bugzilla->user;
sub wrap {
validateWidthAndHeight();
-
+
# We create a Chart object so we can validate the parameters
my $chart = new Bugzilla::Chart($cgi);
-
+
$vars->{'time'} = localtime(time());
$vars->{'imagebase'} = $cgi->canonicalise_query(
use Bugzilla::Constants;
use Bugzilla::Install::Requirements;
-use Bugzilla::Install::Util qw(install_string get_version_and_os
+use Bugzilla::Install::Util qw(install_string get_version_and_os
init_console success);
######################################################################
# Print the help message if that switch was selected.
pod2usage({-verbose => 1, -exitval => 1}) if $switch{'help'};
-# Read in the "answers" file if it exists, for running in
+# Read in the "answers" file if it exists, for running in
# non-interactive mode.
my $answers_file = $ARGV[0];
my $silent = $answers_file && !$switch{'verbose'};
unlink(Bugzilla::Constants::bz_locations()->{datadir} . '/mod_perl_preload');
# Check if the default parameter for urlbase is still set, and if so, give
- # notification that they should go and visit editparams.cgi
+ # notification that they should go and visit editparams.cgi
if (Bugzilla->params->{'urlbase'} eq '') {
print "\n" . get_text('install_urlbase_default') . "\n"
unless $silent;
=item B<--reset-password>=user@domain.com
-Resets the specified user's password. checksetup.pl will prompt you to
+Resets the specified user's password. checksetup.pl will prompt you to
enter a new password for the user.
=item B<--no-templates> (B<-t>)
The code for this is in L<Bugzilla::Install::DB/update_table_definitions>.
-This includes creating the default Classification (using
+This includes creating the default Classification (using
L<Bugzilla::Install/create_default_classification>) and setting up all
the foreign keys for all tables, using L<Bugzilla::DB/bz_setup_foreign_keys>.
$answer{'NO_PAUSE'} = 1
C<NO_PAUSE> means "never stop and prompt the user to hit Enter to continue,
-just go ahead and do things, even if they are potentially dangerous."
+just go ahead and do things, even if they are potentially dangerous."
Don't set this to 1 unless you know what you are doing.
=head1 SEE ALSO
@collist = DEFAULT_COLUMN_LIST;
} else {
if (defined $cgi->param("selected_columns")) {
- @collist = grep { exists $columns->{$_} }
+ @collist = grep { exists $columns->{$_} }
$cgi->param("selected_columns");
}
if (defined $cgi->param('splitheader')) {
$vars->{'message'} = "change_columns";
if ($cgi->param('save_columns_for_search')
- && defined $search && $search->user->id == Bugzilla->user->id)
+ && defined $search && $search->user->id == Bugzilla->user->id)
{
my $params = new Bugzilla::CGI($search->url);
$params->param('columnlist', join(",", @collist));
# If we're running on Microsoft IIS, $cgi->redirect discards
# the Set-Cookie lines. In mod_perl, $cgi->redirect with cookies
# causes the page to be rendered as text/plain.
- # Workaround is to use the old-fashioned redirection mechanism.
+ # Workaround is to use the old-fashioned redirection mechanism.
# See bug 214466 and bug 376044 for details.
- if ($ENV{'MOD_PERL'}
+ if ($ENV{'MOD_PERL'}
|| $ENV{'SERVER_SOFTWARE'} =~ /Microsoft-IIS/
|| $ENV{'SERVER_SOFTWARE'} =~ /Sun ONE Web/)
{
print $cgi->redirect($vars->{'redirect_url'});
exit;
}
-
+
$template->process("global/message.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
exit;
my @values = ();
if ($product ne '-All-') {
$and_product = q{ AND products.name = ?};
- $from_product = q{ INNER JOIN products
+ $from_product = q{ INNER JOIN products
ON bugs.product_id = products.id};
push (@values, $product);
}
# If there were no bugs in the search, return early.
my $query = q{SELECT } .
$dbh->sql_to_days('creation_ts') . q{ AS start_day, } .
- $dbh->sql_to_days('current_date') . q{ AS end_day, } .
- $dbh->sql_to_days("'1970-01-01'") .
- qq{ FROM bugs $from_product
- WHERE } . $dbh->sql_to_days('creation_ts') .
- qq{ IS NOT NULL $and_product
+ $dbh->sql_to_days('current_date') . q{ AS end_day, } .
+ $dbh->sql_to_days("'1970-01-01'") .
+ qq{ FROM bugs $from_product
+ WHERE } . $dbh->sql_to_days('creation_ts') .
+ qq{ IS NOT NULL $and_product
ORDER BY start_day } . $dbh->sql_limit(1);
my ($start, $end, $base) = $dbh->selectrow_array($query, undef, @values);
# Get a list of bugs that were created the previous day, and
# add those bugs to the list of bugs for this product.
- $query = qq{SELECT bug_id
- FROM bugs $from_product
- WHERE bugs.creation_ts < } .
- $dbh->sql_from_days($day - 1) .
- q{ AND bugs.creation_ts >= } .
- $dbh->sql_from_days($day - 2) .
+ $query = qq{SELECT bug_id
+ FROM bugs $from_product
+ WHERE bugs.creation_ts < } .
+ $dbh->sql_from_days($day - 1) .
+ q{ AND bugs.creation_ts >= } .
+ $dbh->sql_from_days($day - 2) .
$and_product . q{ ORDER BY bug_id};
my $bug_ids = $dbh->selectcol_arrayref($query, undef, @values);
# at the beginning of the day. If there were no status/resolution
# changes on or after that day, the status was the same as it
# is today (the "current" value). Otherwise, the status was equal to the
-# first "previous value" entry in the bugs_activity table for that
+# first "previous value" entry in the bugs_activity table for that
# bug made on or after that day.
sub _get_value {
my ($removed, $current, $day, $bug) = @_;
# that one too. Remember, these may be the same.
my $dbh = Bugzilla->switch_to_main_db();
my $shadow_dbh = Bugzilla->switch_to_shadow_db();
-
+
my $serieses = $dbh->selectall_hashref("SELECT series_id, query, creator " .
"FROM series " .
- "WHERE frequency != 0 AND " .
+ "WHERE frequency != 0 AND " .
"MOD(($days_since_epoch + series_id), frequency) = 0",
"series_id");
# We delete from the table beforehand, to avoid SQL errors if people run
# collectstats.pl twice on the same day.
- my $deletesth = $dbh->prepare("DELETE FROM series_data
+ my $deletesth = $dbh->prepare("DELETE FROM series_data
WHERE series_id = ? AND series_date = " .
$dbh->quote($today));
-
+
foreach my $series_id (keys %$serieses) {
# We set up the user for Search.pm's permission checking - each series
# runs with the permissions of its creator.
my @open_status;
my @closed_status;
foreach my $status (@{$vars->{'status'}}) {
- is_open_state($status) ? push(@open_status, $status)
+ is_open_state($status) ? push(@open_status, $status)
: push(@closed_status, $status);
}
$vars->{'open_status'} = \@open_status;
my $cgi = Bugzilla->cgi;
my $template = Bugzilla->template;
- # Determine how the user would like to receive the output;
+ # Determine how the user would like to receive the output;
# default is JavaScript.
my $format = $template->get_format("config", scalar($cgi->param('format')),
scalar($cgi->param('ctype')) || "js");
# Note that if you leave out a category here, it will not be indexed
# in the contents file, even though its HTML POD will still exist.
use constant FILE_TRANSLATION => {
- Files => ['importxml', 'contrib', 'checksetup', 'email_in',
+ Files => ['importxml', 'contrib', 'checksetup', 'email_in',
'install-module', 'sanitycheck', 'jobqueue', 'migrate',
'collectstats'],
Modules => ['bugzilla'],
my @downlines = sort {$a->[-1] cmp $b->[-1]} @category_data;
- # And finally, actually print out the table for this category.
+ # And finally, actually print out the table for this category.
printf $Contents qq[<dt><a name="%s">%s</a></dt>\n<dd>\n],
esc($category), esc($category);
print $Contents '<table class="pod_desc_table">' . "\n";
<td>$description</td>
</tr>
END_HTML
-
+
print $Contents $html;
}
print $Contents "</table></dd>\n\n";
my $full_title = $parser->get_title;
$full_title =~ /^\S+\s+-+\s+(.+)/;
my $description = $1;
-
+
$self->{bugzilla_desc} ||= {};
$self->{bugzilla_desc}->{join('::', @$namelets)} = $description;
return $cgi->param($name);
}
elsif (exists DEFAULTS->{$name}) {
- return ref DEFAULTS->{$name} ? @{ DEFAULTS->{$name} }
+ return ref DEFAULTS->{$name} ? @{ DEFAULTS->{$name} }
: DEFAULTS->{$name};
}
return undef;
return $a->{'bug'}->$sort_by <=> $b->{'bug'}->$sort_by;
}
return $a->{'bug'}->$sort_by cmp $b->{'bug'}->$sort_by;
-
+
}
###############
my $origchangedsince = $changedsince;
detaint_natural($changedsince)
- || ThrowUserError("invalid_changedsince",
+ || ThrowUserError("invalid_changedsince",
{ changedsince => $origchangedsince });
my %total_dups = @{$dbh->selectcol_arrayref(
my $reso_field_id = get_field_id('resolution');
my %since_dups = @{$dbh->selectcol_arrayref(
"SELECT dupe_of, COUNT(dupe)
- FROM duplicates INNER JOIN bugs_activity
- ON bugs_activity.bug_id = duplicates.dupe
- WHERE added = 'DUPLICATE' AND fieldid = ?
+ FROM duplicates INNER JOIN bugs_activity
+ ON bugs_activity.bug_id = duplicates.dupe
+ WHERE added = 'DUPLICATE' AND fieldid = ?
AND bug_when >= "
. $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', '?', 'DAY') .
" GROUP BY dupe_of", {Columns=>[1,2]},
action => "edit",
object => "classifications"});
-ThrowUserError("auth_classification_not_enabled")
+ThrowUserError("auth_classification_not_enabled")
unless Bugzilla->params->{"useclassification"};
#
Bugzilla::Component->check({ product => $product, name => $comp_name });
$vars->{'comp'} = $component;
- $vars->{'initial_cc_names'} =
+ $vars->{'initial_cc_names'} =
join(', ', map($_->login, @{$component->initial_cc}));
$vars->{'product'} = $product;
my $triage_owner = trim($cgi->param('triage_owner') || '');
my @initial_cc = $cgi->param('initialcc');
my $isactive = $cgi->param('isactive');
-
+
my $component =
Bugzilla::Component->check({ product => $product, name => $comp_old_name });
# Calling remove_from_db will check if field can be deleted.
# If the field cannot be deleted, it will throw an error.
$field->remove_from_db();
-
+
$vars->{'field'} = $field;
$vars->{'message'} = 'custom_field_deleted';
-
+
delete_token($token);
$template->process('admin/custom_fields/list.html.tmpl', $vars)
$vars->{'action'} = "insert";
$vars->{'token'} = issue_session_token('add_flagtype');
}
- else {
+ else {
$vars->{'action'} = "update";
$vars->{'token'} = issue_session_token('edit_flagtype');
}
unless ($action) {
my @groups = Bugzilla::Group->get_all;
$vars->{'groups'} = \@groups;
-
+
print $cgi->header();
$template->process("admin/groups/list.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
print $cgi->header();
$template->process("admin/groups/create.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
-
+
exit;
}
print $cgi->header();
$template->process("admin/groups/delete.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
-
+
exit;
}
AND grantor_id = ?
AND grant_type = ?');
- # First item is the type, second is whether or not it's "reverse"
+ # First item is the type, second is whether or not it's "reverse"
# (granted_by) (see _do_add for more explanation).
my %fields = (
members => [GROUP_MEMBERSHIP, 0],
visible_to_me => [GROUP_VISIBLE, 1]
);
while (my ($field, $data) = each %fields) {
- _do_add($group, $changes, $sth_insert, "${field}_add",
+ _do_add($group, $changes, $sth_insert, "${field}_add",
$data->[0], $data->[1]);
_do_remove($group, $changes, $sth_delete, "${field}_remove",
$data->[0], $data->[1]);
exit;
}
-
+
if ($action eq 'add') {
$vars->{'token'} = issue_session_token('add_keyword');
if ($action eq 'del') {
my $milestone = Bugzilla::Milestone->check({ product => $product,
name => $milestone_name });
-
+
$vars->{'milestone'} = $milestone;
$vars->{'product'} = $product;
# classifications enabled)
#
-if (Bugzilla->params->{'useclassification'}
+if (Bugzilla->params->{'useclassification'}
&& !$classification_name
&& !$product_name)
{
}
$vars->{'product'} = $product;
$vars->{'token'} = issue_session_token('delete_product');
-
+
Bugzilla::Hook::process('product_confirm_delete', { vars => $vars });
-
+
$template->process("admin/products/confirm-delete.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
exit;
if ($matchvalue eq 'userid') {
if ($matchstr) {
my $stored_matchstr = $matchstr;
- detaint_natural($matchstr)
+ detaint_natural($matchstr)
|| ThrowUserError('illegal_user_id', {userid => $stored_matchstr});
}
$expr = "profiles.userid";
# Lock tables during the check+update session.
$dbh->bz_start_transaction();
-
+
$editusers || $user->can_see_user($otherUser)
|| ThrowUserError('auth_failure', {reason => "not_visible",
action => "modify",
my $otherUser = check_user($otherUserID, $otherUserLogin);
$otherUserID = $otherUser->id;
- Bugzilla->params->{'allowuserdeletion'}
+ Bugzilla->params->{'allowuserdeletion'}
|| ThrowUserError('users_deletion_disabled');
$editusers || ThrowUserError('auth_failure', {group => "editusers",
action => "delete",
my $activity_userid = "profiles_activity.userid";
if ($action eq 'admin_activity') {
- $editusers || ThrowUserError("auth_failure", { group => "editusers",
- action => "admin_activity",
+ $editusers || ThrowUserError("auth_failure", { group => "editusers",
+ action => "admin_activity",
object => "users" });
($activity_userid, $activity_who) = ($activity_who, $activity_userid);
}
(CASE WHEN (groups.id IN ($grouplist)
AND COUNT(directmember.group_id) = 0
AND COUNT(regexpmember.group_id) = 0
- ) THEN 1 ELSE 0 END)
+ ) THEN 1 ELSE 0 END)
AS derivedmember,
COUNT(directbless.group_id) AS directbless
FROM groups
check_token_data($token, 'add_field_value');
my $created_value = Bugzilla::Field::Choice->type($field)->create({
- value => scalar $cgi->param('value'),
+ value => scalar $cgi->param('value'),
sortkey => scalar $cgi->param('sortkey'),
is_open => scalar $cgi->param('is_open'),
visibility_value_id => scalar $cgi->param('visibility_value_id'),
if ($action eq 'delete') {
check_token_data($token, 'delete_version');
- my $version = Bugzilla::Version->check({ product => $product,
+ my $version = Bugzilla::Version->check({ product => $product,
name => $version_name });
$version->remove_from_db;
delete_token($token);
my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $eventid });
$sth = $dbh->prepare("DELETE FROM whine_schedules "
. "WHERE id=?");
- foreach my $schedule (@$schedules) {
+ foreach my $schedule (@$schedules) {
$sth->execute($schedule->id);
}
# schedules
my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $event_id });
foreach my $schedule (@$schedules) {
- my $mailto_type = $schedule->mailto_is_group ? MAILTO_GROUP
+ my $mailto_type = $schedule->mailto_is_group ? MAILTO_GROUP
: MAILTO_USER;
my $mailto = '';
if ($mailto_type == MAILTO_USER) {
# queries
my $queries = Bugzilla::Whine::Query->match({ eventid => $event_id });
for my $query (@$queries) {
- push @{$events->{$event_id}->{'queries'}},
+ push @{$events->{$event_id}->{'queries'}},
{
'name' => $query->name,
'title' => $query->title,
my ($mail_text) = @_;
debug_print('Parsing Email');
$input_email = Email::MIME->new($mail_text);
-
+
my %fields = %{ $switch{'default'} || {} };
Bugzilla::Hook::process('email_in_before_parse', { mail => $input_email,
fields => \%fields });
if ($body =~ /^\s*@/s) {
my $current_field;
while (my $line = shift @body_lines) {
- # If the sig is starting, we want to keep this in the
- # @body_lines so that we don't keep the sig as part of the
+ # If the sig is starting, we want to keep this in the
+ # @body_lines so that we don't keep the sig as part of the
# comment down below.
if ($line eq SIGNATURE_DELIMITER) {
unshift(@body_lines, $line);
}
sub process_bug {
- my ($fields_in) = @_;
+ my ($fields_in) = @_;
my %fields = %$fields_in;
my $bug_id = $fields{'bug_id'};
data => $data,
});
# If we added a comment, and our comment does not already have a type,
- # and this is our first attachment, then we make the comment an
+ # and this is our first attachment, then we make the comment an
# "attachment created" comment.
if ($comment and !$comment->type and !$update_comment) {
- $comment->set_all({ type => CMT_ATTACHMENT_CREATED,
+ $comment->set_all({ type => CMT_ATTACHMENT_CREATED,
extra_data => $obj->id });
$update_comment = 1;
}
$msg =~ s/^Compilation failed in require.+$//ms;
$msg = html_strip($msg);
my $from = Bugzilla->params->{'mailfrom'};
- my $reply = reply(to => $input_email, from => $from, top_post => 1,
+ my $reply = reply(to => $input_email, from => $from, top_post => 1,
body => "$msg\n");
MessageToMTA($reply->as_string);
}
It can be multiple paragraphs.
- --
+ --
This is a signature line, and will be removed automatically, It will not
be included in the bug description.
#
# enter_bug.cgi
# -------------
-# Displays bug entry form. Bug fields are specified through popup menus,
-# drop-down lists, or text fields. Default for these values can be
+# Displays bug entry form. Bug fields are specified through popup menus,
+# drop-down lists, or text fields. Default for these values can be
# passed in as parameters to the cgi.
#
##############################################################################
if (!$vars->{'disable_guided'}) {
# Purpose: force guided format for newbies
- $cgi->param(-name=>'format', -value=>'guided')
+ $cgi->param(-name=>'format', -value=>'guided')
if !$cgi->param('format') && !$user->in_group('canconfirm');
- $cgi->delete('format')
+ $cgi->delete('format')
if ($cgi->param('format') && ($cgi->param('format') eq "__default__"));
}
} else {
$vars->{'cc'} = formvalue('cc');
}
-
+
if ($cloned_bug->reporter->id != $user->id) {
- $vars->{'cc'} = join (", ", $cloned_bug->reporter->login, $vars->{'cc'});
+ $vars->{'cc'} = join (", ", $cloned_bug->reporter->login, $vars->{'cc'});
}
foreach my $field (@enter_bug_fields) {
$default{'bug_status'} = $statuses[0]->name;
}
else {
- $default{'bug_status'} = ($statuses[0]->name ne 'UNCONFIRMED')
+ $default{'bug_status'} = ($statuses[0]->name ne 'UNCONFIRMED')
? $statuses[0]->name : $statuses[1]->name;
}
$vars->{'default'} = \%default;
my $format = $template->get_format("bug/create/create",
- scalar $cgi->param('format'),
+ scalar $cgi->param('format'),
scalar $cgi->param('ctype'));
print $cgi->header($format->{'ctype'});
$template->process($format->{'template'}, $vars)
- || ThrowTemplateError($template->error());
+ || ThrowTemplateError($template->error());
my @order_columns;
foreach my $o (@orderstrings) {
$o =~ s/bugs.//;
- $o = $db_order_column_name_map{$o} if
+ $o = $db_order_column_name_map{$o} if
grep($_ eq $o, keys(%db_order_column_name_map));
next if (grep($_ eq $o, @order_columns));
push(@order_columns, $o);
$vars->{'order_columns'} = \@order_columns;
- # fields that have a custom sortkey. (So they are correctly sorted
+ # fields that have a custom sortkey. (So they are correctly sorted
# when using js)
my @sortkey_fields = qw(bug_status resolution bug_severity priority
rep_platform op_sys);
my $format = $1;
if (!$vars->{'cloned_bug_id'}) {
# Allow status whiteboard values to be bookmarked
- $vars->{'status_whiteboard'} =
+ $vars->{'status_whiteboard'} =
Bugzilla->cgi->param('status_whiteboard') || "";
}
if ($file =~ /^list\/list/ || $file =~ /^bug\/create\/create[\.-]/) {
- # hack to allow the bug entry templates to use check_can_change_field
+ # hack to allow the bug entry templates to use check_can_change_field
# to see if various field values should be available to the current user.
$vars->{'default'} = Bugzilla::Extension::BMO::FakeBug->new($vars->{'default'} || {});
}
# were changed for better performance and are now only loaded once.
# I have not found an easy way to allow our hook template to check if
# it is called from pages/fields.html.tmpl. So we set a value in request_cache
- # that our hook template can see.
+ # that our hook template can see.
Bugzilla->request_cache->{'bmo_fields_page'} = 1;
}
elsif ($page eq 'query_database.html') {
my $component_name = blessed $component ? $component->name : $component;
my @tmp_fields;
- foreach my $field (@$$fields) {
+ foreach my $field (@$$fields) {
next if cf_hidden_in_product($field->name, $product_name, $component_name);
push(@tmp_fields, $field);
}
? $product_name->name
: $product_name;
- # Also in buglist.cgi, we pass in a list of components instead
+ # Also in buglist.cgi, we pass in a list of components instead
# of a single component name everywhere else.
my $component_list = [];
if ($component_name) {
}
# If product matches and at at least one component matches
- # from component_list (if a matching component was required),
+ # from component_list (if a matching component was required),
# we allow the field to be seen
if ($product eq $product_name && (!@$components || $found_component)) {
return 0;
return 0;
}
-# Purpose: CC certain email addresses on bugmail when a bug is added or
+# Purpose: CC certain email addresses on bugmail when a bug is added or
# removed from a particular group.
sub bugmail_recipients {
my ($self, $args) = @_;
sub _cc_if_special_group {
my ($group, $recipients) = @_;
-
+
return if !$group;
-
+
if (exists $group_change_notification{$group}) {
foreach my $login (@{ $group_change_notification{$group} }) {
my $id = login_to_id($login);
sub _check_trusted {
my ($field, $trusted, $priv_results) = @_;
-
+
my $needed_group = $trusted->{'_default'} || "";
foreach my $dfield (keys %$trusted) {
if ($field =~ $dfield) {
}
if ($needed_group && !Bugzilla->user->in_group($needed_group)) {
push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ }
}
sub _is_field_set {
}
} elsif ($user->in_group('canconfirm', $bug->{'product_id'})) {
- # Canconfirm is really "cantriage"; users with canconfirm can also mark
+ # Canconfirm is really "cantriage"; users with canconfirm can also mark
# bugs as DUPLICATE, WORKSFORME, and INCOMPLETE.
if ($field eq 'bug_status'
&& is_open_state($old_value)
} elsif ($field eq 'bug_status') {
# Disallow reopening of bugs which have been resolved for > 1 year
- if (is_open_state($new_value)
+ if (is_open_state($new_value)
&& !is_open_state($old_value)
- && $bug->resolution eq 'FIXED')
+ && $bug->resolution eq 'FIXED')
{
my $days_ago = DateTime->now(time_zone => Bugzilla->local_timezone);
$days_ago->subtract(days => 365);
# link to hg.m.o
# Note: for grouping in this regexp, always use non-capturing parentheses.
- my $hgrepos = join('|', qw!(?:releases/)?comm-[\w.]+
+ my $hgrepos = join('|', qw!(?:releases/)?comm-[\w.]+
(?:releases/)?mozilla-[\w.]+
(?:releases/)?mobile-[\w.]+
tracemonkey
# our repository. We already have this information in the (static)
# contribute.json file, so parse that in
my $json = JSON::XS->new->pretty->utf8->canonical();
- my $contribute = eval {
+ my $contribute = eval {
$json->decode(scalar read_file(bz_locations()->{cgi_path} . "/contribute.json"));
};
my $commit = `git rev-parse HEAD`;
DEV_ENGAGE_DISCUSS_NEEDINFO
);
-# Maximum attachment size in lines that will be sent with a
+# Maximum attachment size in lines that will be sent with a
# requested attachment flag notification.
use constant REQUEST_MAX_ATTACH_LINES => 1000;
# 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,
+# 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.
#
my ($self, $params) = validate(@_, 'names');
my $dbh = Bugzilla->dbh;
- defined($params->{names})
+ defined($params->{names})
|| ThrowCodeError('params_required',
{ function => 'BMO.getBugsConfirmer', params => ['names'] });
my $query = "SELECT DISTINCT bugs_activity.bug_id
FROM bugs_activity
- LEFT JOIN bug_group_map
+ LEFT JOIN bug_group_map
ON bugs_activity.bug_id = bug_group_map.bug_id
WHERE bugs_activity.fieldid = ?
AND bugs_activity.added = 'NEW'
my ($self, $params) = validate(@_, 'names');
my $dbh = Bugzilla->dbh;
- defined($params->{names})
+ defined($params->{names})
|| ThrowCodeError('params_required',
{ function => 'BMO.getBugsVerifier', params => ['names'] });
my $query = "SELECT DISTINCT bugs_activity.bug_id
FROM bugs_activity
- LEFT JOIN bug_group_map
+ LEFT JOIN bug_group_map
ON bugs_activity.bug_id = bug_group_map.bug_id
WHERE bugs_activity.fieldid = ?
AND bugs_activity.removed = 'RESOLVED'
=head1 METHODS
-See L<Bugzilla::WebService> for a description of how parameters are passed,
+See L<Bugzilla::WebService> for a description of how parameters are passed,
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
=head2 getBugsConfirmer
=item B<Description>
-This method returns public bug ids that a given user has confirmed (changed from
+This method returns public bug ids that a given user has confirmed (changed from
C<UNCONFIRMED> to C<NEW>).
=item B<Params>
=over
-=item C<names> (array) - An array of strings representing Bugzilla login names.
+=item C<names> (array) - An array of strings representing Bugzilla login names.
=back
=item B<Description>
This method returns public bug ids that a given user has verified (changed from
-C<RESOLVED> to C<VERIFIED>).
+C<RESOLVED> to C<VERIFIED>).
=item B<Params>
=over
-=item C<names> (array) - An array of strings representing Bugzilla login names.
+=item C<names> (array) - An array of strings representing Bugzilla login names.
=back
my $user = Bugzilla->user;
- # Disallow a bug from being reopened if currently closed unless 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'))
+ 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)
+ # 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
+ # 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'))
+ && $user->in_group('engineering'))
{
push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
return;
# how to use this hook.
my $bug = $args->{'bug'};
my $timestamp = $args->{'timestamp'};
-
+
my $bug_id = $bug->id;
# Uncomment this line to see a line in your webserver's error log whenever
# you file a bug.
sub bug_end_of_create_validators {
my ($self, $args) = @_;
-
+
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
my $bug_params = $args->{'params'};
-
+
# Uncomment this line below to see a line in your webserver's error log
# containing all validated bug field values every time you file a bug.
# warn Dumper($bug_params);
-
+
# This would remove all ccs from the bug, preventing ANY ccs from being
# added on bug creation.
# $bug_params->{cc} = [];
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
- my ($bug, $old_bug, $timestamp, $changes) =
+ my ($bug, $old_bug, $timestamp, $changes) =
@$args{qw(bug old_bug timestamp changes)};
foreach my $field (keys %$changes) {
sub bug_end_of_update {
my ($self, $args) = @_;
-
+
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
- my ($bug, $old_bug, $timestamp, $changes) =
+ my ($bug, $old_bug, $timestamp, $changes) =
@$args{qw(bug old_bug timestamp changes)};
-
+
foreach my $field (keys %$changes) {
my $used_to_be = $changes->{$field}->[0];
my $now_it_is = $changes->{$field}->[1];
$status_message = "Bug closed!";
}
}
-
+
my $bug_id = $bug->id;
my $num_changes = scalar keys %$changes;
my $result = "There were $num_changes changes to fields on bug $bug_id"
sub bug_format_comment {
my ($self, $args) = @_;
-
+
# This replaces every occurrence of the word "foo" with the word
# "bar"
-
+
my $regexes = $args->{'regexes'};
push(@$regexes, { match => qr/\bfoo\b/, replace => 'bar' });
-
+
# And this links every occurrence of the word "bar" to example.com,
# but it won't affect "foo"s that have already been turned into "bar"
# above (because each regex is run in order, and later regexes don't modify
# Used by bug_format_comment--see its code for an explanation.
sub _replace_bar {
my $args = shift;
- # $match is the first parentheses match in the $bar_match regex
- # in bug-format_comment.pl. We get up to 10 regex matches as
+ # $match is the first parentheses match in the $bar_match regex
+ # in bug-format_comment.pl. We get up to 10 regex matches as
# arguments to this function.
my $match = $args->{matches}->[0];
# Remember, you have to HTML-escape any data that you are returning!
sub buglist_columns {
my ($self, $args) = @_;
-
+
my $columns = $args->{'columns'};
$columns->{'example'} = { 'name' => 'bugs.delta_ts' , 'title' => 'Example' };
$columns->{'product_desc'} = { 'name' => 'prod_desc.description',
sub search_operator_field_override {
my ($self, $args) = @_;
-
+
my $operators = $args->{'operators'};
my $original = $operators->{component}->{_non_changed};
my $recipients = $args->{recipients};
my $bug = $args->{bug};
- my $user =
+ my $user =
new Bugzilla::User({ name => Bugzilla->params->{'maintainer'} });
if ($bug->id == 1) {
sub config_add_panels {
my ($self, $args) = @_;
-
+
my $modules = $args->{panel_modules};
$modules->{Example} = "Bugzilla::Extension::Example::Config";
}
sub config_modify_panels {
my ($self, $args) = @_;
-
+
my $panels = $args->{panels};
-
+
# Add the "Example" auth methods.
my $auth_params = $panels->{'auth'}->{params};
my ($info_class) = grep($_->{name} eq 'user_info_class', @$auth_params);
push(@$auth_params, { name => 'param_example',
type => 't',
default => 0,
- checker => \&check_numeric });
+ checker => \&check_numeric });
}
sub db_schema_abstract_schema {
sub enter_bug_entrydefaultvars {
my ($self, $args) = @_;
-
+
my $vars = $args->{vars};
$vars->{'example'} = 1;
}
sub flag_end_of_update {
my ($self, $args) = @_;
-
+
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
my $flag_params = $args;
$granted++ if $new_flag =~ /\+$/;
$denied++ if $new_flag =~ /-$/;
}
- my $bug_id = $object->isa('Bugzilla::Bug') ? $object->id
+ my $bug_id = $object->isa('Bugzilla::Bug') ? $object->id
: $object->bug_id;
my $result = "$granted flags were granted and $denied flags were denied"
. " on bug $bug_id at $timestamp.";
my $group_id = $group->id;
my $num_changes = scalar keys %$changes;
- my $result =
+ my $result =
"There were $num_changes changes to fields on group $group_id.";
# Uncomment this line to see $result in your webserver's error log whenever
# you update a group.
sub install_before_final_checks {
my ($self, $args) = @_;
print "Install-before_final_checks hook\n" unless $args->{silent};
-
+
# Add a new user setting like this:
#
# add_setting({
# options => ['pretty', 'full', 'small'], # options
# category => 'pretty' # default
# });
- # To add descriptions for the setting and choices, add extra values to
- # the hash defined in global/setting-descs.none.tmpl. Do this in a hook:
+ # To add descriptions for the setting and choices, add extra values to
+ # the hash defined in global/setting-descs.none.tmpl. Do this in a hook:
# hook/global/setting-descs-settings.none.tmpl .
}
# code as well as allow the webserver to server content from it.
# my $data_path = bz_locations->{'datadir'} . "/" . __PACKAGE__->NAME;
# $create_dirs->{$data_path} = Bugzilla::Install::Filesystem::DIR_CGI_WRITE;
-
+
# Update the permissions of any files and directories that currently reside
- # in the extension's directory.
+ # in the extension's directory.
# $recurse_dirs->{$data_path} = {
# files => Bugzilla::Install::Filesystem::CGI_READ,
# dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
# };
-
- # Create a htaccess file that allows specific content to be served from the
+
+ # Create a htaccess file that allows specific content to be served from the
# extension's directory.
# $htaccess->{"$data_path/.htaccess"} = {
# perms => Bugzilla::Install::Filesystem::WS_SERVE,
sub install_update_db_fielddefs {
my $dbh = Bugzilla->dbh;
-# $dbh->bz_add_column('fielddefs', 'example_column',
+# $dbh->bz_add_column('fielddefs', 'example_column',
# {TYPE => 'MEDIUMTEXT', NOTNULL => 1, DEFAULT => ''});
}
sub job_map {
my ($self, $args) = @_;
-
+
my $job_map = $args->{job_map};
-
+
# This adds the named class (an instance of TheSchwartz::Worker) as a
# handler for when a job is added with the name "some_task".
$job_map->{'some_task'} = 'Bugzilla::Extension::Example::Job::SomeClass';
-
+
# Schedule a job like this:
# my $queue = Bugzilla->job_queue();
# $queue->insert('some_task', { some_parameter => $some_variable });
sub mailer_before_send {
my ($self, $args) = @_;
-
+
my $email = $args->{email};
# If you add a header to an email, it's best to start it with
# 'X-Bugzilla-<Extension>' so that you don't conflict with
sub object_before_create {
my ($self, $args) = @_;
-
+
my $class = $args->{'class'};
my $object_params = $args->{'params'};
-
+
# Note that this is a made-up class, for this example.
if ($class->isa('Bugzilla::ExampleObject')) {
warn "About to create an ExampleObject!";
- warn "Got the following parameters: "
+ warn "Got the following parameters: "
. join(', ', keys(%$object_params));
}
}
if ($object->isa('Bugzilla::ExampleObject')) {
my $id = $object->id;
warn "An object with id $id is about to be deleted!";
- }
+ }
}
sub object_before_set {
my ($self, $args) = @_;
-
+
my ($object, $field, $value) = @$args{qw(object field value)};
-
+
# Note that this is a made-up class, for this example.
if ($object->isa('Bugzilla::ExampleObject')) {
- warn "The field $field is changing from " . $object->{$field}
+ warn "The field $field is changing from " . $object->{$field}
. " to $value!";
}
}
sub object_end_of_create {
my ($self, $args) = @_;
-
+
my $class = $args->{'class'};
my $object = $args->{'object'};
sub object_end_of_create_validators {
my ($self, $args) = @_;
-
+
my $class = $args->{'class'};
my $object_params = $args->{'params'};
-
+
# Note that this is a made-up class, for this example.
if ($class->isa('Bugzilla::ExampleObject')) {
# Always set example_field to 1, even if the validators said otherwise.
$object_params->{example_field} = 1;
}
-
+
}
sub object_end_of_set {
sub object_end_of_set_all {
my ($self, $args) = @_;
-
+
my $object = $args->{'object'};
my $object_params = $args->{'params'};
-
+
# Note that this is a made-up class, for this example.
if ($object->isa('Bugzilla::ExampleObject')) {
if ($object_params->{example_field} == 1) {
$object->{example_field} = 1;
}
}
-
+
}
sub object_end_of_update {
my ($self, $args) = @_;
-
- my ($object, $old_object, $changes) =
+
+ my ($object, $old_object, $changes) =
@$args{qw(object old_object changes)};
-
+
# Note that this is a made-up class, for this example.
if ($object->isa('Bugzilla::ExampleObject')) {
if (defined $changes->{'name'}) {
sub page_before_template {
my ($self, $args) = @_;
-
+
my ($vars, $page) = @$args{qw(vars page_id)};
-
+
# You can see this hook in action by loading page.cgi?id=example.html
if ($page eq 'example.html') {
$vars->{cgi_variables} = { Bugzilla->cgi->Vars };
sub post_bug_after_creation {
my ($self, $args) = @_;
-
+
my $vars = $args->{vars};
$vars->{'example'} = 1;
}
sub product_confirm_delete {
my ($self, $args) = @_;
-
+
my $vars = $args->{vars};
$vars->{'example'} = 1;
}
my $example_group = new Bugzilla::Group({ name => 'example_group' });
if ($example_group) {
- $product->set_group_controls($example_group,
+ $product->set_group_controls($example_group,
{ entry => 1,
membercontrol => CONTROLMAPMANDATORY,
othercontrol => CONTROLMAPMANDATORY });
# Bugzilla::Component->create(
# { name => 'No Component',
# product => $product,
-# description => 'Select this component if one does not ' .
+# description => 'Select this component if one does not ' .
# 'exist in the current list of components',
# initialowner => $default_assignee });
}
sub sanitycheck_check {
my ($self, $args) = @_;
-
+
my $dbh = Bugzilla->dbh;
my $sth;
-
+
my $status = $args->{'status'};
-
+
# Check that all users are Australian
$status->('example_check_au_user');
-
+
$sth = $dbh->prepare("SELECT userid, login_name
FROM profiles
WHERE login_name NOT LIKE '%.au'");
$sth->execute;
-
+
my $seen_nonau = 0;
while (my ($userid, $login, $numgroups) = $sth->fetchrow_array) {
$status->('example_check_au_user_alert',
'alert');
$seen_nonau = 1;
}
-
+
$status->('example_check_au_user_prompt') if $seen_nonau;
}
sub sanitycheck_repair {
my ($self, $args) = @_;
-
+
my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh;
-
+
my $status = $args->{'status'};
-
+
if ($cgi->param('example_repair_au_user')) {
$status->('example_repair_au_user_start');
-
+
#$dbh->do("UPDATE profiles
# SET login_name = CONCAT(login_name, '.au')
# WHERE login_name NOT LIKE '%.au'");
-
+
$status->('example_repair_au_user_end');
}
}
sub template_before_create {
my ($self, $args) = @_;
-
+
my $config = $args->{'config'};
# This will be accessible as "example_global_variable" in every
# template in Bugzilla. See Bugzilla/Template.pm's create() function
sub template_before_process {
my ($self, $args) = @_;
-
+
my ($vars, $file, $context) = @$args{qw(vars file context)};
if ($file eq 'bug/edit.html.tmpl') {
#
# The Original Code is the Bugzilla Bug Tracking System.
#
-# Contributor(s):
+# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
%strings = (
$vars->{flag_currently_requested} ||= {};
foreach my $type (@$flag_types) {
my $flags = Bugzilla::Flag->match({
- type_id => $type->id,
- bug_id => $bug->id,
+ type_id => $type->id,
+ bug_id => $bug->id,
status => '?'
});
map { $vars->{flag_currently_requested}->{$_->id} = 1 } @$flags;
);
use constant FLAGTYPE_TEMPLATES => (
- "attachment/edit.html.tmpl",
- "attachment/createformcontents.html.tmpl",
- "bug/edit.html.tmpl",
+ "attachment/edit.html.tmpl",
+ "attachment/createformcontents.html.tmpl",
+ "bug/edit.html.tmpl",
"bug/create/create.html.tmpl"
);
#
# The Original Code is the FlagTypeComment Bugzilla Extension.
#
-# The Initial Developer of the Original Code is Alex Keybl
+# The Initial Developer of the Original Code is Alex Keybl
# Portions created by the Initial Developer are Copyright (C) 2011 the
# Initial Developer. All Rights Reserved.
#
#
# The Original Code is the FlagTypeComment Bugzilla Extension.
#
-# The Initial Developer of the Original Code is Alex Keybl
+# The Initial Developer of the Original Code is Alex Keybl
# Portions created by the Initial Developer are Copyright (C) 2011 the
# Initial Developer. All Rights Reserved.
#
comment => {
TYPE => 'MEDIUMTEXT',
NOTNULL => 1
- },
+ },
],
INDEXES => [
flagtype_comments_idx => ['type_id'],
if ($id) {
$db_result = $dbh->selectall_arrayref(
"SELECT type_id AS flagtype, on_status AS state, comment AS text
- FROM flagtype_comments
+ FROM flagtype_comments
WHERE type_id = ?",
{ Slice => {} }, $id);
}
'product_id' => $bug->product_id,
'component_id' => $bug->component_id,
'bug_id' => $bug->id,
- 'active_or_has_flags' => $bug->id,
+ 'active_or_has_flags' => $bug->id,
});
my $types = join(',', map { $_->id } @$flag_types);
my $states = "'" . join("','", FLAGTYPE_COMMENT_STATES) . "'";
$db_result = $dbh->selectall_arrayref(
"SELECT type_id AS flagtype, on_status AS state, comment AS text
- FROM flagtype_comments
- WHERE type_id IN ($types) AND on_status IN ($states)",
- { Slice => {} });
+ FROM flagtype_comments
+ WHERE type_id IN ($types) AND on_status IN ($states)",
+ { Slice => {} });
}
foreach my $row (@$db_result) {
#
# The Original Code is the FlagTypeComment Bugzilla Extension.
#
-# The Initial Developer of the Original Code is Alex Keybl
+# The Initial Developer of the Original Code is Alex Keybl
# Portions created by the Initial Developer are Copyright (C) 2011 the
# Initial Developer. All Rights Reserved.
#
my $dbh = Bugzilla->dbh;
my $field_id = get_field_id('bug_status');
my $resolved_activity = $dbh->selectall_arrayref(
- "SELECT bugs_activity.bug_id, bugs_activity.bug_when, bugs_activity.who
+ "SELECT bugs_activity.bug_id, bugs_activity.bug_when, bugs_activity.who
FROM bugs_activity
WHERE bugs_activity.fieldid = ?
AND bugs_activity.added = 'RESOLVED'
my ($self, $args) = @_;
my $columns = $args->{columns};
$columns->{'cf_last_resolved'} = {
- name => 'bugs.cf_last_resolved',
- title => 'Last Resolved',
+ name => 'bugs.cf_last_resolved',
+ title => 'Last Resolved',
};
}
=head1 METHODS
-See L<Bugzilla::WebService> for a description of how parameters are passed,
+See L<Bugzilla::WebService> for a description of how parameters are passed,
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
return;
}
- print "Creating needinfo flag ... " .
+ print "Creating needinfo flag ... " .
"enable the Needinfo feature by editing the flag's properties.\n";
# inclusions 0:0 maps to __ANY__ : __ANY__ in the UI,
my ($class, $validators) = @$args{qw(class validators)};
if ($class->isa('Bugzilla::Comment')) {
my $extra_data_validator = $validators->{extra_data};
- $validators->{extra_data} =
+ $validators->{extra_data} =
sub { _check_comment_extra_data($extra_data_validator, @_) };
}
elsif ($class->isa('Bugzilla::Bug')) {
my $dbh = Bugzilla->dbh;
my $template = Bugzilla->template;
- _user_is_mover(Bugzilla->user)
+ _user_is_mover(Bugzilla->user)
or ThrowUserError("auth_failure", { action => 'move',
object => 'bugs' });
my ($self, $args) = @_;
my $panels = $args->{'panels'};
my $auth_panel_params = $panels->{'auth'}->{'params'};
-
- my ($user_info_class) =
+
+ my ($user_info_class) =
grep { $_->{'name'} eq 'user_info_class' } @$auth_panel_params;
if ($user_info_class) {
};
my $result = request('project.search', $data);
- return undef
+ return undef
unless (exists $result->{result}{data} && @{ $result->{result}{data} });
return $result->{result}{data}[0]{phid};
my $dbh = Bugzilla->dbh;
return $dbh->selectrow_array("SELECT COUNT(bug_id)
- FROM bugs
+ FROM bugs
WHERE product_id = ?", undef, $product->id);
}
my $bug_status = shift;
my $dbh = Bugzilla->dbh;
- return $dbh->selectrow_array("SELECT COUNT(bug_id)
- FROM bugs
- WHERE bug_status IN (" . join(',', quoted_open_states()) . ")
+ return $dbh->selectrow_array("SELECT COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . join(',', quoted_open_states()) . ")
AND product_id = ?", undef, $product->id);
}
my $product = shift;
my $dbh = Bugzilla->dbh;
- return $dbh->selectrow_array("SELECT COUNT(bug_id)
- FROM bugs
- WHERE bug_status IN (" . join(',', quoted_closed_states()) . ")
+ return $dbh->selectrow_array("SELECT COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . join(',', quoted_closed_states()) . ")
AND product_id = ?", undef, $product->id);
}
return $dbh->selectall_arrayref("SELECT version, COUNT(bug_id),
ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
- FROM bugs
- WHERE product_id = ?
+ FROM bugs
+ WHERE product_id = ?
$extra
GROUP BY version
ORDER BY COUNT(bug_id) DESC",
return $dbh->selectall_arrayref("SELECT target_milestone, COUNT(bug_id),
ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
- FROM bugs
+ FROM bugs
WHERE product_id = ?
$extra
GROUP BY target_milestone
return $dbh->selectall_arrayref("SELECT priority, COUNT(bug_id),
ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
- FROM bugs
+ FROM bugs
WHERE product_id = ?
$extra
GROUP BY priority
return $dbh->selectall_arrayref("SELECT bug_severity, COUNT(bug_id),
ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
- FROM bugs
- WHERE product_id = ?
+ FROM bugs
+ WHERE product_id = ?
$extra
GROUP BY bug_severity
ORDER BY COUNT(bug_id) DESC",
return $dbh->selectall_arrayref("SELECT components.name, COUNT(bugs.bug_id),
ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
- FROM bugs INNER JOIN components ON bugs.component_id = components.id
+ FROM bugs INNER JOIN components ON bugs.component_id = components.id
WHERE bugs.product_id = ?
$extra
GROUP BY components.name
my ($product, $type, $value, $bug_status) = @_;
my $dbh = Bugzilla->dbh;
- my $query = "SELECT bugs.bug_id AS id,
+ my $query = "SELECT bugs.bug_id AS id,
bugs.bug_status AS status,
bugs.version AS version,
components.name AS component,
if ($type eq 'component') {
Bugzilla::Component->check({ product => $product, name => $value });
$query .= "AND components.name = ? " if $type eq 'component';
- }
+ }
elsif ($type eq 'version') {
Bugzilla::Version->check({ product => $product, name => $value });
$query .= "AND bugs.version = ? " if $type eq 'version';
my $timestamp = $dbh->selectrow_array("SELECT " . $dbh->sql_date_format("LOCALTIMESTAMP(0)", "%Y-%m-%d"));
- return {
+ return {
timestamp => $timestamp,
past_due => _filter_bugs($past_due_bugs),
updated_recently => _filter_bugs($updated_recently_bugs),
AND bugs.assigned_to = profiles.userid
$extra
GROUP BY profiles.login_name
- ORDER BY COUNT(bugs.bug_id) DESC $limit",
+ ORDER BY COUNT(bugs.bug_id) DESC $limit",
undef, $product->id, $product->id)};
return \@result;
ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs
WHERE bugs.product_id = ?
- $extra
+ $extra
GROUP BY bugs.bug_status
ORDER BY COUNT(bugs.bug_id) DESC",
undef, $product->id, $product->id);
my ($product, $milestone) = @_;
my $dbh = Bugzilla->dbh;
- return $dbh->selectrow_array("SELECT COUNT(bug_id)
- FROM bugs
- WHERE target_milestone = ?
+ return $dbh->selectrow_array("SELECT COUNT(bug_id)
+ FROM bugs
+ WHERE target_milestone = ?
AND product_id = ?",
undef, $milestone->name, $product->id);
}
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
return $dbh->selectrow_array("SELECT COUNT(bug_id)
- FROM bugs
+ FROM bugs
WHERE target_milestone = ?
AND product_id = ? $extra",
undef,
sub by_popularity {
my ($product, $bug_status, $limit) = @_;
my $dbh = Bugzilla->dbh;
- $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
+ $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
my $extra = '';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
push(@values, $days);
}
- my $unfiltered_bugs = $dbh->selectall_arrayref("SELECT DISTINCT bugs.bug_id AS id,
+ my $unfiltered_bugs = $dbh->selectall_arrayref("SELECT DISTINCT bugs.bug_id AS id,
bugs.bug_status AS status,
bugs.version AS version,
components.name AS component,
sub bug_link_open {
my $product = shift;
- return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
+ return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
"&bug_status=__open__";
}
sub bug_link_closed {
my $product = shift;
- return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
+ return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
"&bug_status=__closed__";
}
sub bug_milestone_link_total {
my ($product, $milestone) = @_;
- return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
+ return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
"&target_milestone=" . url_quote($milestone->name);
}
sub bug_milestone_link_open {
my ($product, $milestone) = @_;
- return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
+ return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
"&target_milestone=" . url_quote($milestone->name) . "&bug_status=__open__";
}
sub bug_milestone_link_closed {
my ($product, $milestone) = @_;
- return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
+ return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
"&target_milestone=" . url_quote($milestone->name) . "&bug_status=__closed__";
}
# it tries to stop an arms race starting.)
if ($comment &&
!$comment->author->in_group('editbugs') &&
- $comment->author->id != Bugzilla->user->id)
+ $comment->author->id != Bugzilla->user->id)
{
push (@$regexes, {
match => RE_profanity('-i'),
$author = new Bugzilla::User({ name => $author });
if ($author &&
- $author->id &&
+ $author->id &&
!$author->in_group('editbugs'))
{
# Multipart emails
#
# The Original Code is the REMO Bugzilla Extension.
#
-# The Initial Developer of the Original Code is Mozilla Foundation
+# The Initial Developer of the Original Code is Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2011 the
# Initial Developer. All Rights Reserved.
#
=item C<real_name> (string)
-The real name of the bugzilla user.
+The real name of the bugzilla user.
=item C<name> (string)
my $recurse_dirs = $args->{'recurse_dirs'};
my $htaccess = $args->{'htaccess'};
- # Create the sitemap directory to store the index and sitemap files
+ # Create the sitemap directory to store the index and sitemap files
my $sitemap_path = bz_locations->{'datadir'} . "/" . __PACKAGE__->NAME;
$create_dirs->{$sitemap_path} = Bugzilla::Install::Filesystem::DIR_CGI_WRITE
| Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE;
- $recurse_dirs->{$sitemap_path} = {
+ $recurse_dirs->{$sitemap_path} = {
files => Bugzilla::Install::Filesystem::CGI_WRITE
| Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE,
- dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
- | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE
+ dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
+ | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE
};
# Create a htaccess file that allows the sitemap files to be served out
- $htaccess->{"$sitemap_path/.htaccess"} = {
+ $htaccess->{"$sitemap_path/.htaccess"} = {
perms => Bugzilla::Install::Filesystem::WS_SERVE,
contents => <<EOT
# Allow access to sitemap files created by the SiteMapIndex extension
use constant SITEMAP_AGE => 12;
# This is the largest number of entries that can be in a single sitemap file,
-# per the sitemaps.org standard.
+# per the sitemaps.org standard.
use constant SITEMAP_MAX => 50_000;
# We only show bugs that are at least 12 hours old, because if somebody
-# files a bug that's a security bug but doesn't protect it, we want to give
+# files a bug that's a security bug but doesn't protect it, we want to give
# them time to fix that.
use constant SITEMAP_DELAY => 12;
use base qw(Exporter);
our @EXPORT = qw(
- generate_sitemap
+ generate_sitemap
bug_is_ok_to_index
);
'SELECT bugs.bug_id, bugs.delta_ts
FROM bugs
LEFT JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id
- WHERE bug_group_map.bug_id IS NULL AND creation_ts < ?
+ WHERE bug_group_map.bug_id IS NULL AND creation_ts < ?
' . $dbh->sql_limit($num_bugs, '?'));
my $filecount = 1;
push(@$filelist, _generate_sitemap_file($extension_name, $filecount, $products, $bugs));
$filecount++;
- $offset += $num_bugs;
+ $offset += $num_bugs;
}
# Generate index file
sub _generate_sitemap_index {
my ($extension_name, $filelist) = @_;
-
+
my $dbh = Bugzilla->dbh;
my $timestamp = $dbh->selectrow_array(
"SELECT " . $dbh->sql_date_format('NOW()', '%Y-%m-%d'));
<changefreq>daily</changefreq>
<priority>0.4</priority>
</url>
-";
+";
}
foreach my $bug (@$bugs) {
<url>
<loc>" . $bug_url . $bug->{bug_id} . "</loc>
<lastmod>" . datetime_from($bug->{delta_ts}, 'UTC')->iso8601 . 'Z' . "</lastmod>
- </url>
+ </url>
";
}
# Write the compressed sitemap data to a file in the cgi root so that they can
# be accessed by the search engines.
my $filename = "sitemap$filecount.xml.gz";
- gzip \$sitemap_xml => bz_locations->{'datadir'} . "/$extension_name/$filename"
- || die "gzip failed: $GzipError\n";
+ gzip \$sitemap_xml => bz_locations->{'datadir'} . "/$extension_name/$filename"
+ || die "gzip failed: $GzipError\n";
return $filename;
}
sub bug_format_comment {
my ($self, $args) = @_;
-
+
my $bug = $args->{'bug'};
my $regexes = $args->{'regexes'};
my $text = $args->{'text'};
-
+
# Add [review] link to the end of "Created attachment" comments
#
# We need to work around the way that the hook works, which is intended
replace => get_review_link("$2", "[review]") })) &&
(attachment_id_is_patch($2) ? "$1 __REVIEW__$2" : $1)
~egmx;
-
+
# And linkify "Review of attachment", this is less of a workaround since
# there is no issue with overlap; note that there is an assumption that
# there is only one match in the text we are linkifying, since they all
sub mailer_before_send {
my ($self, $args) = @_;
-
+
# Post-process bug mail to add review links to bug mail.
# It would be nice to be able to hook in earlier in the
# process when the email body is being formatted in the
sub attachment_id_is_valid {
my ($attach_id, $dont_validate_access) = @_;
- # Validate the specified attachment id.
+ # Validate the specified attachment id.
detaint_natural($attach_id) || return 0;
# Make sure the attachment exists in the database.
my $attachment = new Bugzilla::Attachment({ id => $attach_id, cache => 1 })
|| return 0;
- return $attachment
+ return $attachment
if ($dont_validate_access || attachment_is_visible($attachment));
}
$attachment->isa('Bugzilla::Attachment') || return 0;
- return (Bugzilla->user->can_see_bug($attachment->bug->id)
- && (!$attachment->isprivate
- || Bugzilla->user->id == $attachment->attacher->id
+ return (Bugzilla->user->can_see_bug($attachment->bug->id)
+ && (!$attachment->isprivate
+ || Bugzilla->user->id == $attachment->attacher->id
|| Bugzilla->user->is_insider));
}
my $attachment = attachment_id_is_valid($attach_id);
if (attachment_id_is_patch($attach_id)) {
- return "<a href='" . html_quote(get_review_url($attachment->bug, $attach_id)) .
+ return "<a href='" . html_quote(get_review_url($attachment->bug, $attach_id)) .
"'>$link_text</a>";
}
else {
return ("$intro_text" .
" View: $view_link\015\012" .
" Review: " . get_review_url($bug, $attach_id, 1) . "\015\012");
- }
+ }
else {
return ("$intro_text --> ($view_link)");
}
my $new_body = 0;
my $bug;
- if ($email->header('Subject') =~ /^\[Bug\s+(\d+)\]/
+ if ($email->header('Subject') =~ /^\[Bug\s+(\d+)\]/
&& Bugzilla->user->can_see_bug($1))
{
$bug = Bugzilla::Bug->new({ id => $1, cache => 1 });
SELECT COUNT(*)
FROM attachments
WHERE submitter_id = ?
- AND (ispatch = 1
+ AND (ispatch = 1
OR mimetype IN ('text/x-github-pull-request',
'text/x-review-board-request',
'text/x-phabricator-request'))
$dbh->bz_add_column('products', 'votesperuser',
{TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
- $dbh->bz_add_column('products', 'maxvotesperbug',
+ $dbh->bz_add_column('products', 'maxvotesperbug',
{TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
$dbh->bz_add_column('products', 'votestoconfirm',
{TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
my ($self) = @_;
return $self->{'user_votes'} if exists $self->{'user_votes'};
$self->{'user_votes'} = Bugzilla->dbh->selectrow_array(
- "SELECT vote_count FROM votes WHERE bug_id = ? AND who = ?",
+ "SELECT vote_count FROM votes WHERE bug_id = ? AND who = ?",
undef, $self->id, Bugzilla->user->id);
return $self->{'user_votes'};
}
my ($self, $args) = @_;
my ($object, $changes) = @$args{qw(object changes)};
if ( $object->isa('Bugzilla::Product')
- and ($changes->{maxvotesperbug} or $changes->{votesperuser}
- or $changes->{votestoconfirm}) )
+ and ($changes->{maxvotesperbug} or $changes->{votesperuser}
+ or $changes->{votestoconfirm}) )
{
_modify_bug_votes($object, $changes);
}
if ($canedit && $bug) {
# Make sure there is an entry for this bug
# in the vote table, just so that things display right.
- my $has_votes = $dbh->selectrow_array('SELECT vote_count FROM votes
+ my $has_votes = $dbh->selectrow_array('SELECT vote_count FROM votes
WHERE bug_id = ? AND who = ?',
undef, ($bug->id, $who->id));
if (!$has_votes) {
- $dbh->do('INSERT INTO votes (who, bug_id, vote_count)
+ $dbh->do('INSERT INTO votes (who, bug_id, vote_count)
VALUES (?, ?, 0)', undef, ($who->id, $bug->id));
}
}
my $input = Bugzilla->input_params;
# Build a list of bug IDs for which votes have been submitted. Votes
- # are submitted in form fields in which the field names are the bug
+ # are submitted in form fields in which the field names are the bug
# IDs and the field values are the number of votes.
my @buglist = grep {/^\d+$/} keys %$input;
$vars->{'title_tag'} = 'change_votes';
foreach my $bug_id (@updated_bugs) {
$vars->{'id'} = $bug_id;
- $vars->{'sent_bugmail'} =
+ $vars->{'sent_bugmail'} =
Bugzilla::BugMail::Send($bug_id, { 'changer' => $user });
-
+
$template->process("bug/process/results.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
# Set header_done to 1 only after the first bug.
foreach my $bug_id (@$bug_ids) {
# _remove_votes returns a list of messages to send
# in case some voters had too many votes.
- push(@msgs, _remove_votes($bug_id, $who,
+ push(@msgs, _remove_votes($bug_id, $who,
'votes_too_many_per_user'));
my $name = user_id_to_login($who);
# 3. enough votes to confirm
my $bug_list = $dbh->selectcol_arrayref(
- 'SELECT bug_id FROM bugs
+ 'SELECT bug_id FROM bugs
WHERE product_id = ? AND bug_status = ? AND votes >= ?',
undef, ($product->id, 'UNCONFIRMED', $product->{votestoconfirm}));
ThrowUserError('extension_first_letter_caps', { name => $name });
}
-my $extension_dir = "$base_dir/$name";
-mkpath($extension_dir)
+my $extension_dir = "$base_dir/$name";
+mkpath($extension_dir)
|| die "$extension_dir already exists or cannot be created.\n";
my $lcname = lc($name);
-foreach my $path (qw(lib web template/en/default/hook),
+foreach my $path (qw(lib web template/en/default/hook),
"template/en/default/$lcname")
{
mkpath("$extension_dir/$path") || die "$extension_dir/$path: $!";
}
use lib qw(. lib local/lib/perl5);
-# Data dumber is used for debugging, I got tired of copying it back in
-# and then removing it.
+# Data dumber is used for debugging, I got tired of copying it back in
+# and then removing it.
#use Data::Dumper;
if ( !$requestee->can_see_bug($bugid) ) {
$err .= "Requestee is not a member of bug group\n";
$err .= " Requesting from the wind\n";
- }
+ }
else{
$requestee_id = $requestee->id;
}
$err = "Invalid requestee $requestee_login on $type flag $name\n";
$err .= " Requesting from the wind.\n";
}
-
+
}
my $flag_types;
if ($ftype) { # We found the flag in the list
my $grant_group = $ftype->grant_group;
- if (( $status eq '+' || $status eq '-' )
+ if (( $status eq '+' || $status eq '-' )
&& $grant_group && !$setter->in_group_id($grant_group->id)) {
$err = "Setter $setter_login on $type flag $name ";
$err .= "is not in the Grant Group\n";
return $err;
}
- $dbh->do("INSERT INTO flags
- (type_id, status, bug_id, attach_id, creation_date,
+ $dbh->do("INSERT INTO flags
+ (type_id, status, bug_id, attach_id, creation_date,
setter_id, requestee_id)
VALUES (?, ?, ?, ?, ?, ?, ?)", undef,
($ftype->id, $status, $bugid, $attachid, $timestamp,
Error( "invalid exporter: $exporter", "REOPEN", $exporter ) if ( !login_to_id($exporter) );
Error( "no urlbase set", "REOPEN", $exporter ) unless ($urlbase);
}
-
+
# Parse attachments.
#
elsif ($encoding =~ /filename/) {
# read the attachment file
Error("attach_path is required", undef) unless ($attach_path);
-
+
my $filename = $attach->field('data');
# Remove any leading path data from the filename
$filename =~ s/(.*\/|.*\\)//gs;
-
+
my $attach_filename = $attach_path . "/" . $filename;
open(ATTACH_FH, "<", $attach_filename) or
Error("cannot open $attach_filename", undef);
# This list contains all other bug fields that we want to process.
# If it is not in this list it will not be included.
my %all_fields;
- foreach my $field (
+ foreach my $field (
qw(long_desc attachment flag group), Bugzilla::Bug::fields() )
{
$all_fields{$field} = 1;
}
-
+
my %bug_fields;
my $err = "";
# Alias
if ( $bug_fields{'alias'} ) {
- my ($alias) = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs
+ my ($alias) = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs
WHERE alias = ?", undef,
$bug_fields{'alias'} );
if ($alias) {
$product = new Bugzilla::Product({ name => $default_product_name })
or Error("an invalid default product was defined for the target"
. " DB. " . $params->{"maintainer"} . " needs to specify "
- . "--product when calling importxml.pl", "REOPEN",
+ . "--product when calling importxml.pl", "REOPEN",
$exporter);
}
my $component = new Bugzilla::Component({
$component = new Bugzilla::Component({
name => $default_component_name, product => $product });
if (!$component) {
- Error("an invalid default component was defined for the target"
- . " DB. ". $params->{"maintainer"} . " needs to specify "
- . "--component when calling importxml.pl", "REOPEN",
+ Error("an invalid default component was defined for the target"
+ . " DB. ". $params->{"maintainer"} . " needs to specify "
+ . "--component when calling importxml.pl", "REOPEN",
$exporter);
}
}
}
# Status & Resolution
- my $valid_res = check_field('resolution',
- scalar $bug_fields{'resolution'},
+ my $valid_res = check_field('resolution',
+ scalar $bug_fields{'resolution'},
undef, ERR_LEVEL );
- my $valid_status = check_field('bug_status',
- scalar $bug_fields{'bug_status'},
+ my $valid_status = check_field('bug_status',
+ scalar $bug_fields{'bug_status'},
undef, ERR_LEVEL );
- my $is_open = is_open_state($bug_fields{'bug_status'});
+ my $is_open = is_open_state($bug_fields{'bug_status'});
my $status = $bug_fields{'bug_status'} || undef;
my $resolution = $bug_fields{'resolution'} || undef;
-
- # Check everconfirmed
+
+ # Check everconfirmed
my $everconfirmed;
if ($product->allows_unconfirmed) {
$everconfirmed = $bug_fields{'everconfirmed'} || 0;
$resolution = "INVALID";
$err .= "This bug was marked DUPLICATE in the database ";
$err .= "it was moved from.\n Changing resolution to \"INVALID\"\n";
- }
+ }
# If there is at least 1 initial bug status different from UNCO, use it,
# else use the open bug status with the lowest sortkey (different from UNCO).
$resolution = undef;
}
if($changed_owner){
- if($everconfirmed){
+ if($everconfirmed){
$status = $initial_status;
}
else{
$err .= " Setting resolution to INVALID\n";
$resolution = "INVALID";
}
- }
+ }
}
else{ # $valid_status is false
- if($everconfirmed){
+ if($everconfirmed){
$status = $initial_status;
}
else{
$status = "UNCONFIRMED";
- }
+ }
$err .= "Bug has invalid status, setting status to \"$status\".\n";
$err .= " Previous status was \"";
$err .= $bug_fields{'bug_status'} . "\".\n";
}
}
else {
- if($everconfirmed){
+ if($everconfirmed){
$status = $initial_status;
}
else{
$status = "UNCONFIRMED";
- }
+ }
$err .= "Bug has no status, setting status to \"$status\".\n";
$err .= " Previous status was unknown\n";
$resolution = undef;
push( @query, "resolution" );
push( @values, $resolution );
}
-
+
# Bug status
push( @query, "bug_status" );
push( @values, $status );
if ( defined( $bug_fields{'keywords'} ) ) {
my %keywordseen;
my $key_sth = $dbh->prepare(
- "INSERT INTO keywords
+ "INSERT INTO keywords
(bug_id, keywordid) VALUES (?,?)"
);
foreach my $keyword ( split( /[\s,]+/, $bug_fields{'keywords'} )) {
my $attacher_id = $att->{'attacher'} ? login_to_id($att->{'attacher'}) : undef;
- $dbh->do("INSERT INTO attachments
+ $dbh->do("INSERT INTO attachments
(bug_id, creation_ts, modification_time, filename, description,
- mimetype, ispatch, isprivate, isobsolete, submitter_id)
+ mimetype, ispatch, isprivate, isobsolete, submitter_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
undef, $id, $att->{'date'}, $att->{'date'}, $att->{'filename'},
$att->{'desc'}, $att->{'ctype'}, $att->{'ispatch'},
$att->{'isprivate'}, $att->{'isobsolete'}, $attacher_id || $exporterid);
my $att_id = $dbh->bz_last_key( 'attachments', 'attach_id' );
my $att_data = $att->{'data'};
- my $sth = $dbh->prepare("INSERT INTO attach_data (id, thedata)
+ my $sth = $dbh->prepare("INSERT INTO attach_data (id, thedata)
VALUES ($att_id, ?)" );
trick_taint($att_data);
$sth->bind_param( 1, $att_data, $dbh->BLOB_TYPE );
Bugzilla::Bug->new($id)->_sync_fulltext( new_bug => 1);
# Add this bug to each group of which its product is a member.
- my $sth_group = $dbh->prepare("INSERT INTO bug_group_map (bug_id, group_id)
+ my $sth_group = $dbh->prepare("INSERT INTO bug_group_map (bug_id, group_id)
VALUES (?, ?)");
foreach my $group_id ( keys %{ $product->group_controls } ) {
if ($product->group_controls->{$group_id}->{'membercontrol'} != CONTROLMAPNA
# It is time to email the result of the import.
my $log = join("\n\n", @logs);
$log .= "\n\nImported $bugtotal bug(s) from $urlbase,\n sent by $exporter.\n";
-my $subject = "$bugtotal Bug(s) successfully moved from $urlbase to "
+my $subject = "$bugtotal Bug(s) successfully moved from $urlbase to "
. $params->{"urlbase"};
my @to = ($exporter, $maintainer);
MailMessage( $subject, $log, @to );
First using the move function of bugzilla
on another system will send mail to an alias provided by
the administrator of the target installation (you). Set up an alias
- similar to the one given below so this mail will be automatically
+ similar to the one given below so this mail will be automatically
run by this script and imported into your database. Run 'newaliases'
after adding this alias to your aliases file. Make sure your sendmail
- installation is configured to allow mail aliases to execute code.
+ installation is configured to allow mail aliases to execute code.
bugzilla-import: "|/usr/bin/perl /opt/bugzilla/importxml.pl --mail"
- Second it can be run from the command line with any xml file from
- STDIN that conforms to the bugzilla DTD. In this case you can pass
+ Second it can be run from the command line with any xml file from
+ STDIN that conforms to the bugzilla DTD. In this case you can pass
an argument to set whether you want to send the
mail that will be sent to the exporter and maintainer normally.
print $cgi->header();
$template->process("bug/new_bug.html.tmpl",
$vars)
- or ThrowTemplateError($template->error());
+ or ThrowTemplateError($template->error());
Bugzilla->metrics->name("page.cgi $id");
}
- my %vars = (
+ my %vars = (
quicksearch_field_names => \&quicksearch_field_names,
);
- Bugzilla::Hook::process('page_before_template',
+ Bugzilla::Hook::process('page_before_template',
{ page_id => $id, vars => \%vars });
my $format = $template->get_format("pages/$1", undef, $2);
-
+
$cgi->param('id', $id);
print $cgi->header($format->{'ctype'});
if (defined $cgi->param('maketemplate')) {
$vars->{'url'} = $cgi->canonicalise_query('token');
$vars->{'short_desc'} = $cgi->param('short_desc');
-
+
print $cgi->header();
$template->process("bug/create/make-template.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
$attachment->set_flags($flags, $new_flags);
$attachment->update($timestamp);
my $comment = $bug->comments->[0];
- $comment->set_all({ type => CMT_ATTACHMENT_CREATED,
+ $comment->set_all({ type => CMT_ATTACHMENT_CREATED,
extra_data => $attachment->id });
$comment->update();
}
# a checkbox.
my ($field, $check_defined) = @_;
my $cgi = Bugzilla->cgi;
- if ( defined $cgi->param($field)
+ if ( defined $cgi->param($field)
|| ($check_defined && defined $cgi->param("defined_$field")) )
{
return 1;
};
}
if (should_set('see_also')) {
- $set_all_fields{'see_also'}->{add} =
+ $set_all_fields{'see_also'}->{add} =
[split(/[\s,]+/, $cgi->param('see_also'))];
}
if (should_set('remove_see_also')) {
if (defined $cgi->param('newcc')
or defined $cgi->param('addselfcc')
or defined $cgi->param('removecc')
- or defined $cgi->param('masscc'))
+ or defined $cgi->param('masscc'))
{
my (@cc_add, @cc_remove);
# If masscc is defined, then we came from buglist and need to either add or
if ($userid) {
$userdefaultquery = $dbh->selectrow_array(
"SELECT query FROM namedqueries " .
- "WHERE userid = ? AND name = ?",
+ "WHERE userid = ? AND name = ?",
undef, ($userid, DEFAULT_QUERY_NAME));
}
next if grep { $_ eq $name } @skip;
$foundone = 1;
my @values = $buf->param($name);
-
+
# If the name is a single letter followed by numbers, it's part
# of Custom Search. We store these as an array of hashes.
if ($name =~ /^([[:lower:]])(\d+)$/) {
}
}
-# if using groups for entry, then we don't want people to see products they
+# if using groups for entry, then we don't want people to see products they
# don't have access to. Remove them from the list.
-my @selectable_products = sort {lc($a->name) cmp lc($b->name)}
+my @selectable_products = sort {lc($a->name) cmp lc($b->name)}
@{$user->get_selectable_products};
Bugzilla::Product::preload(\@selectable_products);
# If we submit back to ourselves (for e.g. boolean charts), we need to
# preserve format information; hence query_format taking priority over
# format.
-my $format = $template->get_format("search/search",
- $vars->{'query_format'} || $vars->{'format'},
+my $format = $template->get_format("search/search",
+ $vars->{'query_format'} || $vars->{'format'},
scalar $cgi->param('ctype'));
print $cgi->header($format->{'ctype'});
my @quipids;
foreach my $quipref (@$quipsref) {
my ($quipid, $userid, $quip, $approved) = @$quipref;
- $quips->{$quipid} = {'userid' => $userid, 'quip' => $quip,
+ $quips->{$quipid} = {'userid' => $userid, 'quip' => $quip,
'approved' => $approved};
push(@quipids, $quipid);
}
ThrowUserError("no_new_quips");
check_hash_token($token, ['create-quips']);
- # Add the quip
+ # Add the quip
# Upstreaming: https://bugzilla.mozilla.org/show_bug.cgi?id=621879
my $approved = (Bugzilla->params->{'quip_list_entry_control'} eq "open")
|| $user->in_group('bz_quip_moderators') || 0;
check_hash_token($token, ['approve-quips']);
# Read in the entire quip list
my $quipsref = $dbh->selectall_arrayref("SELECT quipid, approved FROM quips");
-
+
my %quips;
foreach my $quipref (@$quipsref) {
my ($quipid, $approved) = @$quipref;
my $quip = $cgi->param("quipid_$quipid") ? 1 : 0;
if(defined($cgi->param("defined_quipid_$quipid"))) {
if($quips{$quipid} != $quip) {
- if($quip) {
- push(@approved, $quipid);
- } else {
- push(@unapproved, $quipid);
+ if($quip) {
+ push(@approved, $quipid);
+ } else {
+ push(@unapproved, $quipid);
}
}
}
action => "delete",
object => "quips"});
my $quipid = $cgi->param("quipid");
- ThrowCodeError("need_quipid") unless $quipid =~ /(\d+)/;
+ ThrowCodeError("need_quipid") unless $quipid =~ /(\d+)/;
$quipid = $1;
check_hash_token($token, ['quips', $quipid]);
ThrowUserError('sudo_in_progress', { target => $user->login });
}
- # Did the user actually go trough the 'sudo-prepare' action? Do some
+ # Did the user actually go trough the 'sudo-prepare' action? Do some
# checks on the token the action should have left.
my ($token_user, $token_timestamp, $token_data) = Bugzilla::Token::GetTokenData($token);
unless (defined($token_user)
&& ($token_user == $user->id)
&& ($token_data eq 'sudo_prepared'))
{
- ThrowUserError('sudo_preparation_required',
+ ThrowUserError('sudo_preparation_required',
{ target_login => $target_login, reason => $reason });
}
delete_token($cgi->param('token'));
delete_token($token);
# NOTE: If you want to log the end of an sudo session, so it here.
-
+
$vars->{'message'} = 'sudo_ended';
$target = 'global/message.html.tmpl';
}
# Go straight back to query.cgi if we are adding a boolean chart.
if (grep(/^cmd-/, $cgi->param())) {
my $params = $cgi->canonicalise_query("format", "ctype");
- my $location = "query.cgi?format=" . $cgi->param('query_format') .
+ my $location = "query.cgi?format=" . $cgi->param('query_format') .
($params ? "&$params" : "");
print $cgi->redirect($location);
my $formatparam = $cgi->param('format') || '';
-# These shenanigans are necessary to make sure that both vertical and
+# These shenanigans are necessary to make sure that both vertical and
# horizontal 1D tables convert to the correct dimension when you ask to
# display them as some sort of chart.
if ($formatparam eq "table") {
- if ($col_field && !$row_field) {
+ if ($col_field && !$row_field) {
# 1D *tables* should be displayed vertically (with a row_field only)
$row_field = $col_field;
$col_field = '';
my $valid_columns = Bugzilla::Search::REPORT_COLUMNS;
# Validate the values in the axis fields or throw an error.
-!$row_field
+!$row_field
|| ($valid_columns->{$row_field} && trick_taint($row_field))
|| ThrowCodeError("report_axis_invalid", {fld => "x", val => $row_field});
-!$col_field
+!$col_field
|| ($valid_columns->{$col_field} && trick_taint($col_field))
|| ThrowCodeError("report_axis_invalid", {fld => "y", val => $col_field});
-!$tbl_field
+!$tbl_field
|| ($valid_columns->{$tbl_field} && trick_taint($tbl_field))
|| ThrowCodeError("report_axis_invalid", {fld => "z", val => $tbl_field});
# Clone the params, so that Bugzilla::Search can modify them
my $params = new Bugzilla::CGI($cgi);
my $search = new Bugzilla::Search(
- fields => \@axis_fields,
+ fields => \@axis_fields,
params => scalar $params->Vars,
allow_unlimited => 1,
);
Bugzilla->switch_to_shadow_db();
my ($results, $extra_data) = $search->data;
-# We have a hash of hashes for the data itself, and a hash to hold the
+# We have a hash of hashes for the data itself, and a hash to hold the
# row/col/table names.
my %data;
my %names;
$names{"col"}{$col}++;
$names{"row"}{$row}++;
$names{"tbl"}{$tbl}++;
-
+
$col_isnumeric &&= ($col =~ /^-?\d+(\.\d+)?$/o);
$row_isnumeric &&= ($row =~ /^-?\d+(\.\d+)?$/o);
$tbl_isnumeric &&= ($tbl =~ /^-?\d+(\.\d+)?$/o);
# gathered everything into the hashes and made sure we know the size of the
# data, we reformat it into an array of arrays of arrays of data.
push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1);
-
+
my @image_data;
foreach my $tbl (@tbl_names) {
my @tbl_data;
if ($tbl ne "-total-") {
# This is a bit sneaky. We spend every loop except the last
# building up the -total- data, and then last time round,
- # we process it as another tbl, and push() the total values
+ # we process it as another tbl, and push() the total values
# into the image_data array.
$data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row};
}
push(@tbl_data, \@col_data);
}
-
+
unshift(@image_data, \@tbl_data);
}
$vars->{'format'} = $formatparam;
$formatparam = '';
- # We need to keep track of the defined restrictions on each of the
+ # We need to keep track of the defined restrictions on each of the
# axes, because buglistbase, below, throws them away. Without this, we
# get buglistlinks wrong if there is a restriction on an axis field.
$vars->{'col_vals'} = get_field_restrictions($col_field);
$vars->{'buglistbase'} = $cgi->canonicalise_query(
"x_axis_field", "y_axis_field", "z_axis_field",
"ctype", "format", "query_format", @axis_fields);
- $vars->{'imagebase'} = $cgi->canonicalise_query(
+ $vars->{'imagebase'} = $cgi->canonicalise_query(
$tbl_field, "action", "ctype", "format", "width", "height");
- $vars->{'switchbase'} = $cgi->canonicalise_query(
+ $vars->{'switchbase'} = $cgi->canonicalise_query(
"query_format", "action", "ctype", "format", "width", "height");
$vars->{'data'} = \%data;
}
# _realname fields aren't real Bugzilla::Field objects, but they are a
# valid axis, so we don't vailidate them as Bugzilla::Field objects.
- $field = Bugzilla::Field->check($field_name)
+ $field = Bugzilla::Field->check($field_name)
if ($field_name && $field_name !~ /_realname$/);
-
+
if ($field && $field->is_select) {
foreach my $value (@{$field->legal_values}) {
push(@sorted, $value->name) if $names->{$value->name};
}
unshift(@sorted, '---') if $field_name eq 'resolution';
@sorted = uniq @sorted;
- }
+ }
elsif ($isnumeric) {
- # It's not a field we are preserving the order of, so sort it
+ # It's not a field we are preserving the order of, so sort it
# numerically...
@sorted = sort { $a <=> $b } keys %$names;
}
# ...or alphabetically, as appropriate.
@sorted = sort keys %$names;
}
-
+
return @sorted;
}
if (!$product_name) {
# Can we do bug charts?
- (-d $dir && -d $graph_dir)
+ (-d $dir && -d $graph_dir)
|| ThrowCodeError('chart_dir_nonexistent',
{dir => $dir, graph_dir => $graph_dir});
my $date = $line[0];
my ($yy, $mm, $dd) = $date =~ /^\d{2}(\d{2})(\d{2})(\d{2})$/;
push @{$data{DATE}}, "$mm/$dd/$yy";
-
+
for my $i (1 .. $#fields) {
my $field = $fields[$i];
if (! defined $line[$i] or $line[$i] eq '') {
}
}
}
-
+
shift @labels;
close FILE;
dataset4 => [0, 0, 0], # black
},
);
-
+
$img->set (%settings);
$img->png($image_file, [ @data{('DATE', @labels)} ]);
}
my $status = validateStatus($cgi->param('status'));
my $form_group = validateGroup($cgi->param('group'));
- my $query =
+ my $query =
# Select columns describing each flag, the bug/attachment on which
# it has been set, who set it, and of whom they are requesting it.
" SELECT flags.id, flagtypes.name,
requesters.realname, requesters.login_name,
requestees.realname, requestees.login_name, COUNT(privs.group_id),
" . $dbh->sql_date_format('flags.modification_date', '%Y.%m.%d %H:%i') . ",
- attachments.ispatch,
- bugs.bug_status,
- bugs.priority,
- bugs.bug_severity " .
+ attachments.ispatch,
+ bugs.bug_status,
+ bugs.priority,
+ bugs.bug_severity " .
# Use the flags and flagtypes tables for information about the flags,
# the bugs and attachments tables for target info, the profiles tables
# for setter and requestee info, the products/components tables
# table to help us weed out secure bugs to which the user should not have
# access.
"
- FROM flags
+ FROM flags
LEFT JOIN attachments
ON flags.attach_id = attachments.attach_id
INNER JOIN flagtypes
foreach my $result (@$results) {
my @data = @$result;
my $request = {
- 'id' => $data[0] ,
- 'type' => $data[1] ,
- 'status' => $data[2] ,
- 'bug_id' => $data[3] ,
- 'bug_summary' => $data[4] ,
- 'category' => "$data[5]: $data[6]" ,
- 'attach_id' => $data[7] ,
+ 'id' => $data[0] ,
+ 'type' => $data[1] ,
+ 'status' => $data[2] ,
+ 'bug_id' => $data[3] ,
+ 'bug_summary' => $data[4] ,
+ 'category' => "$data[5]: $data[6]" ,
+ 'attach_id' => $data[7] ,
'attach_summary' => $data[8] ,
- 'requester' => ($data[9] ? "$data[9] <$data[10]>" : $data[10]) ,
- 'requestee' => ($data[11] ? "$data[11] <$data[12]>" : $data[12]) ,
+ 'requester' => ($data[9] ? "$data[9] <$data[10]>" : $data[10]) ,
+ 'requestee' => ($data[11] ? "$data[11] <$data[12]>" : $data[12]) ,
'restricted' => $data[13] ? 1 : 0,
- 'created' => $data[14],
- 'ispatch' => $data[15],
- 'bug_status' => $data[16],
- 'priority' => $data[17],
- 'bug_severity' => $data[18],
+ 'created' => $data[14],
+ 'ispatch' => $data[15],
+ 'bug_status' => $data[16],
+ 'priority' => $data[17],
+ 'bug_severity' => $data[18],
};
push(@requests, $request);
}
# 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 Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Mike Norton.
# Portions created by the Initial Developer are Copyright (C) 2002
my $list = $dbh->selectcol_arrayref(qq{
SELECT bug_id
- FROM bugs
+ FROM bugs
WHERE (lastdiffed IS NULL
OR lastdiffed < delta_ts)
AND delta_ts < $time
###########################################################################
# Perform double field referential (cross) checks
###########################################################################
-
+
# This checks that a compound two-field foreign key has a valid primary key
# value. NULL references are acceptable and cause no problem.
#
{table => $refertable, field1 => $referfield1, field2 =>$referfield2});
my $d_cross_check = $dbh->selectall_arrayref(qq{
- SELECT DISTINCT $refertable.$referfield1,
+ SELECT DISTINCT $refertable.$referfield1,
$refertable.$referfield2 } .
($keyname ? qq{, $refertable.$keyname } : q{}) .
qq{ FROM $refertable
LEFT JOIN $table
ON $refertable.$referfield1 = $table.$field1
- AND $refertable.$referfield2 = $table.$field2
- WHERE $table.$field1 IS NULL
- AND $table.$field2 IS NULL
- AND $refertable.$referfield1 IS NOT NULL
+ AND $refertable.$referfield2 = $table.$field2
+ WHERE $table.$field1 IS NULL
+ AND $table.$field2 IS NULL
+ AND $refertable.$referfield1 IS NOT NULL
AND $refertable.$referfield2 IS NOT NULL});
foreach my $check (@$d_cross_check) {
DoubleCrossCheck("versions", "product_id", "value",
["bugs", "product_id", "version", "bug_id"]);
-
+
DoubleCrossCheck("milestones", "product_id", "value",
["bugs", "product_id", "target_milestone", "bug_id"],
["products", "id", "defaultmilestone", "name"]);
sub BugCheck {
my ($middlesql, $errortext, $repairparam, $repairtext) = @_;
my $dbh = Bugzilla->dbh;
-
+
my $badbugs = $dbh->selectcol_arrayref(qq{SELECT DISTINCT bugs.bug_id
- FROM $middlesql
+ FROM $middlesql
ORDER BY bugs.bug_id});
if (scalar(@$badbugs)) {
my $groups = join(", ", (CONTROLMAPNA, CONTROLMAPSHOWN, CONTROLMAPDEFAULT,
CONTROLMAPMANDATORY));
my $query = qq{
- SELECT COUNT(product_id)
- FROM group_control_map
+ SELECT COUNT(product_id)
+ FROM group_control_map
WHERE membercontrol NOT IN( $groups )
OR othercontrol NOT IN( $groups )
OR ((membercontrol != othercontrol)
my $time = $dbh->sql_date_math('NOW()', '-', 30, 'MINUTE');
my $badbugs = $dbh->selectcol_arrayref(qq{
- SELECT bug_id
- FROM bugs
+ SELECT bug_id
+ FROM bugs
WHERE (lastdiffed IS NULL OR lastdiffed < delta_ts)
AND delta_ts < $time
ORDER BY bug_id});
# Begin Data/Security Validation
###############################################################################
-# Check whether or not the user is currently logged in.
+# Check whether or not the user is currently logged in.
Bugzilla->login();
# Make sure the bug ID is a positive integer representing an existing
# Work out which fields we are displaying (currently XML only.)
# If no explicit list is defined, we show all fields. We then exclude any
-# on the exclusion list. This is so you can say e.g. "Everything except
+# on the exclusion list. This is so you can say e.g. "Everything except
# attachments" without listing almost all the fields.
my @fieldlist = (Bugzilla::Bug->fields, 'flag', 'group', 'long_desc',
'attachment', 'attachmentdata', 'token');
}
foreach ($cgi->param("excludefield")) {
- $displayfields{$_} = undef;
+ $displayfields{$_} = undef;
}
$vars->{'displayfields'} = \%displayfields;
my (%seen, %edgesdone, %bugtitles);
my $bug_count = 0;
-# $CreateImagemap: This sub grabs a local filename as a parameter, reads the
+# $CreateImagemap: This sub grabs a local filename as a parameter, reads the
# dot-generated image map datafile residing in that file and turns it into
# an HTML map element. THIS SUB IS ONLY USED FOR LOCAL DOT INSTALLATIONS.
# The map datafile won't necessarily contain the bug summaries, so we'll
print $fh "$k\n";
}
- # Push the bug tooltip texts into a global hash so that
+ # Push the bug tooltip texts into a global hash so that
# $CreateImagemap sub (used with local dot installations) can
# use them later on.
$bugtitles{$k} = trim("$stat $resolution");
- # Show the bug summary in tooltips only if not shown on
+ # Show the bug summary in tooltips only if not shown on
# the graph and it is non-empty (the user can see the bug)
if (!$cgi->param('showsummary') && $summary ne "") {
$bugtitles{$k} .= " - $summary";
# need to make that into a relative path.
my $cgi_root = bz_locations()->{cgi_path};
$pngfilename =~ s#^\Q$cgi_root\E/?##;
-
+
$vars->{'image_url'} = $pngfilename;
# Then, generate a imagemap datafile that contains the corner data
#
sub date_adjust_down {
-
+
my ($year, $month, $day) = @_;
if ($day == 0) {
}
if (($month == 4 || $month == 6 || $month == 9 || $month == 11) &&
- ($day == 31) )
+ ($day == 31) )
{
$day = 30;
}
if (($month == 4 || $month == 6 || $month == 9 || $month == 11) &&
($day == 31) )
{
- $month += 1;
+ $month += 1;
$day = 1;
}
for (my $i=0; $i < $md; $i++) {
# Start of interval is adjusted up: 31.2. -> 1.3.
($year_tmp, $month_tmp, $sd_tmp) = date_adjust_up($year, $month, $sd);
- $sub_start = sprintf("%04d-%02d-%02d", $year_tmp, $month_tmp, $sd_tmp);
+ $sub_start = sprintf("%04d-%02d-%02d", $year_tmp, $month_tmp, $sd_tmp);
$month += 1;
if ($month == 13) {
$month = 1;
$sub_end = sprintf("%04d-%02d-%02d", $year_tmp, $month_tmp, $sd_tmp);
push @months, [$sub_start, $sub_end];
}
-
- # This section handles the last (unfinished) month.
+
+ # This section handles the last (unfinished) month.
$sub_end = sprintf("%04d-%02d-%02d", $ey + 1900, $em + 1, $ed);
($year_tmp, $month_tmp, $sd_tmp) = date_adjust_up($year, $month, $sd);
$sub_start = sprintf("%04d-%02d-%02d", $year_tmp, $month_tmp, $sd_tmp);
trick_taint($start_date);
$date_bits = " AND longdescs.bug_when > ?";
push @date_values, $start_date;
- }
+ }
if ($end_date) {
# we need to add one day to end_date to catch stuff done today
# do not forget to adjust date if it was the last day of month
($ey, $em, $ed) = date_adjust_up($ey+1900, $em+1, $ed+1);
$end_date = sprintf("%04d-%02d-%02d", $ey, $em, $ed);
- $date_bits .= " AND longdescs.bug_when < ?";
+ $date_bits .= " AND longdescs.bug_when < ?";
push @date_values, $end_date;
}
return ($date_bits, \@date_values);
|| ThrowUserError('illegal_date', {date => $date, format => 'YYYY-MM-DD'});
}
# Swap dates in case the user put an end_date before the start_date
- if ($start_date && $end_date &&
+ if ($start_date && $end_date &&
str2time($start_date) > str2time($end_date)) {
$vars->{'warn_swap_dates'} = 1;
($start_date, $end_date) = ($end_date, $start_date);
# Provide a default end date. Note that this differs in semantics
# from the open-ended queries we use when start/end_date aren't
# provided -- and clock skews will make this evident!
- @parts = split_by_month($start_date,
+ @parts = split_by_month($start_date,
$end_date || format_time(scalar localtime(time()), '%Y-%m-%d'));
} else {
@parts = ([$start_date, $end_date]);
],
'flag/list.html.tmpl' => [
- 'flag.status',
- 'type.id',
+ 'flag.status',
+ 'type.id',
],
'search/form.html.tmpl' => [
],
'request/queue.html.tmpl' => [
- 'column_headers.$group_field',
- 'column_headers.$column',
- 'request.status',
- 'request.bug_id',
- 'request.attach_id',
+ 'column_headers.$group_field',
+ 'column_headers.$column',
+ 'request.status',
+ 'request.bug_id',
+ 'request.attach_id',
],
'reports/keywords.html.tmpl' => [
- 'keyword.bug_count',
+ 'keyword.bug_count',
],
'reports/report-table.csv.tmpl' => [
],
'reports/report-table.html.tmpl' => [
- '"&$col_vals" IF col_vals',
- '"&$row_vals" IF row_vals',
- 'classes.$row_idx.$col_idx',
- 'urlbase',
- 'data.$tbl.$col.$row',
+ '"&$col_vals" IF col_vals',
+ '"&$row_vals" IF row_vals',
+ 'classes.$row_idx.$col_idx',
+ 'urlbase',
+ 'data.$tbl.$col.$row',
'row_total',
'col_totals.$col',
- 'grand_total',
+ 'grand_total',
],
'reports/report.html.tmpl' => [
- 'width',
- 'height',
- 'imageurl',
- 'formaturl',
- 'other_format.name',
- 'sizeurl',
+ 'width',
+ 'height',
+ 'imageurl',
+ 'formaturl',
+ 'other_format.name',
+ 'sizeurl',
'switchbase',
'cumulate',
],
'reports/chart.html.tmpl' => [
- 'width',
- 'height',
- 'imageurl',
- 'sizeurl',
- 'height + 100',
- 'height - 100',
- 'width + 100',
- 'width - 100',
+ 'width',
+ 'height',
+ 'imageurl',
+ 'sizeurl',
+ 'height + 100',
+ 'height - 100',
+ 'width + 100',
+ 'width - 100',
],
'reports/series-common.html.tmpl' => [
- 'sel.name',
- '"onchange=\"$sel.onchange\"" IF sel.onchange',
+ 'sel.name',
+ '"onchange=\"$sel.onchange\"" IF sel.onchange',
],
'reports/chart.csv.tmpl' => [
- 'data.$j.$i',
+ 'data.$j.$i',
'colsepchar',
],
'reports/create-chart.html.tmpl' => [
- 'series.series_id',
+ 'series.series_id',
'newidx',
],
'reports/edit-series.html.tmpl' => [
- 'default.series_id',
+ 'default.series_id',
],
'list/edit-multiple.html.tmpl' => [
- 'group.id',
+ 'group.id',
'menuname',
],
'list/list.rdf.tmpl' => [
- 'template_version',
- 'bug.bug_id',
- 'column',
+ 'template_version',
+ 'bug.bug_id',
+ 'column',
],
'list/table.html.tmpl' => [
'tableheader',
- 'bug.bug_id',
+ 'bug.bug_id',
],
'list/list.csv.tmpl' => [
- 'bug.bug_id',
+ 'bug.bug_id',
'colsepchar',
],
'list/list.js.tmpl' => [
- 'bug.bug_id',
+ 'bug.bug_id',
],
'global/choose-product.html.tmpl' => [
'target',
],
-# You are not permitted to add any values here. Everything in this file should
+# You are not permitted to add any values here. Everything in this file should
# be filtered unless there's an extremely good reason why not, in which case,
# use the "none" dummy filter.
'global/code-error.html.tmpl' => [
],
-
+
'global/header.html.tmpl' => [
- 'javascript',
- 'style',
+ 'javascript',
+ 'style',
'onload',
'title',
'" – $header" IF header',
'subheader',
- 'header_addl_info',
- 'message',
+ 'header_addl_info',
+ 'message',
],
'global/messages.html.tmpl' => [
],
'global/select-menu.html.tmpl' => [
- 'options',
- 'size',
+ 'options',
+ 'size',
],
'global/tabs.html.tmpl' => [
- 'content',
+ 'content',
],
-# You are not permitted to add any values here. Everything in this file should
+# You are not permitted to add any values here. Everything in this file should
# be filtered unless there's an extremely good reason why not, in which case,
# use the "none" dummy filter.
'global/user-error.html.tmpl' => [
],
'global/site-navigation.html.tmpl' => [
- 'bug.bug_id',
+ 'bug.bug_id',
],
'bug/comments.html.tmpl' => [
'bug/dependency-graph.html.tmpl' => [
'image_map', # We need to continue to make sure this is safe in the CGI
- 'image_url',
- 'map_url',
- 'bug_id',
+ 'image_url',
+ 'map_url',
+ 'bug_id',
],
'bug/dependency-tree.html.tmpl' => [
- 'bugid',
- 'maxdepth',
- 'hide_resolved',
+ 'bugid',
+ 'maxdepth',
+ 'hide_resolved',
'ids.join(",")',
- 'maxdepth + 1',
+ 'maxdepth + 1',
'maxdepth > 0 && maxdepth <= realdepth ? maxdepth : ""',
'maxdepth == 1 ? 1
: ( maxdepth ? maxdepth - 1 : realdepth - 1 )',
],
'bug/edit.html.tmpl' => [
- 'bug.remaining_time',
- 'bug.delta_ts',
- 'bug.bug_id',
- 'group.bit',
+ 'bug.remaining_time',
+ 'bug.delta_ts',
+ 'bug.bug_id',
+ 'group.bit',
'selname',
'inputname',
'" colspan=\"$colspan\"" IF colspan',
],
'bug/show-multiple.html.tmpl' => [
- 'attachment.id',
+ 'attachment.id',
'flag.status',
],
],
'bug/show.xml.tmpl' => [
- 'constants.BUGZILLA_VERSION',
- 'a.id',
- 'field',
+ 'constants.BUGZILLA_VERSION',
+ 'a.id',
+ 'field',
],
'bug/summarize-time.html.tmpl' => [
'bug/time.html.tmpl' => [
"time_unit.replace('0\\Z', '')",
- '(act / (act + rem)) * 100
- FILTER format("%d")',
+ '(act / (act + rem)) * 100
+ FILTER format("%d")',
],
'bug/process/results.html.tmpl' => [
- 'title.$type',
+ 'title.$type',
'"$terms.Bug $id" FILTER bug_link(id)',
'"$terms.bug $id" FILTER bug_link(id)',
],
'bug/create/create-guided.html.tmpl' => [
'tablecolour',
'sel',
- 'productstring',
+ 'productstring',
],
'bug/activity/table.html.tmpl' => [
- 'change.attachid',
+ 'change.attachid',
],
'attachment/create.html.tmpl' => [
'bug.bug_id',
- 'attachment.id',
+ 'attachment.id',
],
'attachment/created.html.tmpl' => [
],
'attachment/edit.html.tmpl' => [
- 'attachment.id',
- 'attachment.bug_id',
+ 'attachment.id',
+ 'attachment.bug_id',
'editable_or_hide',
],
'attachment/list.html.tmpl' => [
- 'attachment.id',
+ 'attachment.id',
'flag.status',
'bugid',
'obsolete_attachments',
],
'admin/params/common.html.tmpl' => [
- 'sortlist_separator',
+ 'sortlist_separator',
],
'admin/products/groupcontrol/confirm-edit.html.tmpl' => [
- 'group.count',
+ 'group.count',
],
'admin/products/groupcontrol/edit.html.tmpl' => [
'group.id',
- 'constants.CONTROLMAPNA',
+ 'constants.CONTROLMAPNA',
'constants.CONTROLMAPSHOWN',
'constants.CONTROLMAPDEFAULT',
'constants.CONTROLMAPMANDATORY',
],
'admin/products/list.html.tmpl' => [
- 'classification_url_part',
+ 'classification_url_part',
],
'admin/products/footer.html.tmpl' => [
- 'classification_url_part',
- 'classification_text',
+ 'classification_url_part',
+ 'classification_text',
],
'admin/flag-type/confirm-delete.html.tmpl' => [
'flag_type.flag_count',
- 'flag_type.id',
+ 'flag_type.id',
],
'admin/flag-type/edit.html.tmpl' => [
],
'admin/flag-type/list.html.tmpl' => [
- 'type.id',
+ 'type.id',
],
to continue.
END
cpanfile_created => "##file## created",
- cpan_bugzilla_home =>
+ cpan_bugzilla_home =>
"WARNING: Using the Bugzilla directory as the CPAN home.",
db_enum_setup => "Setting up choices for standard drop-down fields:",
db_schema_init => "Initializing bz_schema...",
files for you, which will keep this file (localconfig) and other
confidential files from being read over the web.
-If this is set to 1, checksetup.pl will create .htaccess files if
+If this is set to 1, checksetup.pl will create .htaccess files if
they don't exist.
If this is set to 0, checksetup.pl will not create .htaccess files.
a normal webserver environment.
If set to 1, checksetup.pl will set file permissions so that Bugzilla
-works in a SuexecUserGroup environment.
+works in a SuexecUserGroup environment.
END
localconfig_webservergroup => <<'END',
The name of the group that your web server runs as. On Red Hat
-distributions, this is usually "apache". On Debian/Ubuntu, it is
+distributions, this is usually "apache". On Debian/Ubuntu, it is
usually "www-data".
If you have use_suexec turned on below, then this is instead the name
min_version_required => "Minimum version required: ",
# Note: When translating these "modules" messages, don't change the formatting
-# if possible, because there is hardcoded formatting in
+# if possible, because there is hardcoded formatting in
# Bugzilla::Install::Requirements to match the box formatting.
modules_message_apache => <<END,
***********************************************************************
deleted manually to conserve disk space.
END
template_removing_dir => "Removing existing compiled templates...",
- update_cf_invalid_name =>
+ update_cf_invalid_name =>
"Removing custom field '##field##', because it has an invalid name...",
update_flags_bad_name => <<'END',
"##flag##" is not a valid name for a flag. Rename it to not have any spaces
END
update_nomail_bad => <<'END',
WARNING: The following users were listed in ##data##/nomail, but do
-not have an account here. The unmatched entries have been moved to
+not have an account here. The unmatched entries have been moved to
##data##/nomail.bad:
END
- update_summary_truncate_comment =>
+ update_summary_truncate_comment =>
"The original value of the Summary field was longer than 255"
. " characters, and so it was truncated during an upgrade."
. " The original summary was:\n\n##summary##",
# Check $webservergroup against the server's GID
if ($sgid > 0) {
if ($webservergroup eq "") {
- print
+ print
"WARNING \$webservergroup is set to an empty string.
That is a very insecure practice. Please refer to the
Bugzilla documentation.\n";
print "TEST-OK Webserver is running under group id in \$webservergroup.\n";
}
else {
- print
+ print
"TEST-WARNING Webserver is running under group id not matching \$webservergroup.
This if the tests below fail, this is probably the problem.
-Please refer to the web server configuration section of the Bugzilla guide.
+Please refer to the web server configuration section of the Bugzilla guide.
If you are using virtual hosts or suexec, this warning may not apply.\n";
}
}
if (fetch($url)) {
print "TEST-OK Got padlock picture.\n";
} else {
- print
+ print
"TEST-FAILED Fetch of images/padlock.png failed
Your web server could not fetch $url.
Check your web server configuration and try again.\n";
if ($response =~ /^OK (.*)$/) {
print "TEST-OK Webserver is executing CGIs via $1.\n";
} elsif ($response =~ /^#!/) {
- print
+ print
"TEST-FAILED Webserver is fetching rather than executing CGI files.
Check the AddHandler statement in your httpd.conf file.\n";
exit(1);
} else {
- print "TEST-FAILED Webserver is not executing CGI files.\n";
+ print "TEST-FAILED Webserver is not executing CGI files.\n";
}
# Make sure that the web server is honoring .htaccess files
$url = $ARGV[0] . "/$localconfig";
$response = fetch($url);
if ($response) {
- print
+ print
"TEST-FAILED Webserver is permitting fetch of $url.
This is a serious security problem.
Check your web server configuration.\n";
}
else {
my $gd = $GD::VERSION;
-
+
my $verstring = "GD version $gd, libgd version $gdlib";
-
+
$gdlib =~ s/^([^\.]+)\..*/$1/;
$gd =~ s/^([^\.]+)\..*/$1/;
-
+
if ($gdlib == $gd) {
print "TEST-OK $verstring; Major versions match.\n";
} else {
verify_mfa_login($token);
} elsif ($action eq 'mfa_p') {
verify_mfa_password($token);
-} else {
+} else {
ThrowUserError('unknown_action', {action => $action});
}
if(lc($cgi->param('email')) ne lc($old_email)) {
ThrowUserError("email_confirmation_failed");
}
- # The new email address should be available as this was
+ # The new email address should be available as this was
# confirmed initially so cancel token if it is not still available
if (! is_available_username($new_email,$old_email)) {
$vars->{'email'} = $new_email; # Needed for Bugzilla::Token::Cancel's mail
Bugzilla::Token::Cancel($token, "account_exists", $vars);
ThrowUserError("account_exists", { email => $new_email } );
- }
+ }
# Update the user's login name in the profiles table and delete the token
# from the tokens table.
my $actualemail = $dbh->selectrow_array(
q{SELECT login_name FROM profiles
WHERE userid = ?}, undef, $userid);
-
+
# check to see if it has been altered
if($actualemail ne $old_email) {
# XXX - This is NOT safe - if A has change to B, another profile
$user->derive_regexp_groups;
$vars->{'message'} = "email_change_canceled_reinstated";
- }
- }
+ }
+ }
else {
$vars->{'message'} = 'email_change_canceled'
}
$cgi->delete('passwd1', 'passwd2');
my $otheruser = Bugzilla::User->create({
- login_name => $login_name,
+ login_name => $login_name,
realname => scalar $cgi->param('realname'),
cryptpassword => $password});
local our $vars = {};
###############################################################################
-# Each panel has two functions - panel Foo has a DoFoo, to get the data
-# necessary for displaying the panel, and a SaveFoo, to save the panel's
-# contents from the form data (if appropriate).
-# SaveFoo may be called before DoFoo.
+# Each panel has two functions - panel Foo has a DoFoo, to get the data
+# necessary for displaying the panel, and a SaveFoo, to save the panel's
+# contents from the form data (if appropriate).
+# SaveFoo may be called before DoFoo.
###############################################################################
sub DoAccount {
my $dbh = Bugzilla->dbh;
($vars->{'realname'}) = $dbh->selectrow_array(
"SELECT realname FROM profiles WHERE userid = ?", undef, $user->id);
- if(Bugzilla->params->{'allowemailchange'}
+ if(Bugzilla->params->{'allowemailchange'}
&& Bugzilla->user->authorizer->can_change_email) {
# First delete old tokens.
Bugzilla::Token::CleanTokenTable();
sub DoEmail {
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
-
+
###########################################################################
# User watching
###########################################################################
}
}
}
-
+
# Negative events: a ticked box means "don't send me mail."
foreach my $event (NEG_EVENTS) {
my $is_set = $cgi->param("neg-email-$rel-$event");
# Remove people who were removed.
my $delete_sth = $dbh->prepare('DELETE FROM watch WHERE watched = ?'
. ' AND watcher = ?');
-
+
my %remove_watch_ids;
foreach my $username (@removed) {
my $watched_userid = login_to_id(trim($username), THROW_ERROR);
$dbh->bz_start_transaction();
if (scalar @$removed) {
- $dbh->do('DELETE FROM email_bug_ignore WHERE user_id = ? AND ' .
+ $dbh->do('DELETE FROM email_bug_ignore WHERE user_id = ? AND ' .
$dbh->sql_in('bug_id', $removed),
undef, $user->id);
}
}
$vars->{'has_bits'} = \@has_bits;
- $vars->{'set_bits'} = \@set_bits;
+ $vars->{'set_bits'} = \@set_bits;
}
# No SavePermissions() because this panel has no changeable fields.
$sth_insert_ngm->execute($q->id, $group_id);
}
- # If we're sharing our query with a group we can bless, we
+ # If we're sharing our query with a group we can bless, we
# have the ability to add link to our search to the footer of
# direct group members automatically.
if ($user->can_bless($group_id) && $cgi->param('force_' . $q->id)) {
if ($action eq "show_bug") {
$cgi->delete('action');
$cgi->param('id', 'voting/bug.html');
-}
+}
elsif ($action eq "show_user" or $action eq 'vote') {
$cgi->delete('action') unless $action eq 'vote';
$cgi->param('id', 'voting/user.html');
my $fromaddress = Bugzilla->params->{'mailfrom'};
# get the current date and time
-my ($now_sec, $now_minute, $now_hour, $now_day, $now_month, $now_year,
+my ($now_sec, $now_minute, $now_hour, $now_day, $now_month, $now_year,
$now_weekday) = localtime;
# Convert year to two digits
$now_year = sprintf("%02d", $now_year % 100);
# the target time is less than the current time
else { # set it for the next applicable day
$day = &get_next_date($day);
- my $run_next = $dbh->sql_date_math('('
+ my $run_next = $dbh->sql_date_math('('
. $dbh->sql_date_math('CURRENT_DATE', '+', '?', 'DAY')
. ')', '+', '?', 'HOUR');
$sth = $dbh->prepare("UPDATE whine_schedules " .
# midnight
my $target_time = ($time =~ /^\d+$/) ? $time : 0;
- my $run_next = $dbh->sql_date_math('('
- . $dbh->sql_date_math('CURRENT_DATE', '+', '?', 'DAY')
+ my $run_next = $dbh->sql_date_math('('
+ . $dbh->sql_date_math('CURRENT_DATE', '+', '?', 'DAY')
. ')', '+', '?', 'HOUR');
$sth = $dbh->prepare("UPDATE whine_schedules " .
"SET run_next = $run_next
# 5. Return an event hashref
#
# The event hashref consists of:
-# eventid - ID of the event
+# eventid - ID of the event
# author - user object for the event's creator
# users - array of user objects for recipients
# subject - Subject line for the email
if ($minute_offset > 0) {
# Scheduling is done in terms of whole minutes.
-
+
my $next_run = $dbh->selectrow_array(
'SELECT ' . $dbh->sql_date_math('NOW()', '+', '?', 'MINUTE'),
undef, $minute_offset);
# defined by the Mozilla Public License, v. 2.0.
-# This is a script suitable for running once a day from a cron job. It
-# looks at all the bugs, and sends whiny mail to anyone who has a bug
+# This is a script suitable for running once a day from a cron job. It
+# looks at all the bugs, and sends whiny mail to anyone who has a bug
# assigned to them that has status CONFIRMED, NEW, or REOPENED that has not
# been touched for more than the number of days specified in the whinedays
# param. (We have NEW and REOPENED in there to keep compatibility with old
local @INC = (bz_locations()->{extensionsdir}, @INC);
my $server = new Bugzilla::WebService::Server::XMLRPC;
-# We use a sub for on_action because that gets us the info about what
-# class is being called. Note that this is a hack--this is technically
+# We use a sub for on_action because that gets us the info about what
+# class is being called. Note that this is a hack--this is technically
# for setting SOAPAction, which isn't used by XML-RPC.
$server->on_action(sub { $server->handle_login(WS_DISPATCH, @_) })
->handle();
# This @{ [] } thing is the only reasonable way to get a count out of a
# constant array.
- my $special_tests = scalar(@{ [SPECIAL_PARAM_TESTS, CUSTOM_SEARCH_TESTS] })
+ my $special_tests = scalar(@{ [SPECIAL_PARAM_TESTS, CUSTOM_SEARCH_TESTS] })
* TESTS_PER_RUN;
-
+
return $operator_field_tests + $sql_injection_tests + $special_tests;
}
sub _total_operator_tests {
my ($self, $operators) = @_;
-
+
# Some operators have more than one test. Find those ones and add
# them to the total operator tests
my $extra_operator_tests;
$extra_operator_tests += $extra_num;
}
return scalar(@$operators) + $extra_operator_tests;
-
+
}
sub all_operators {
my ($self) = @_;
if (not $self->{all_operators}) {
-
+
my @operators;
if (my $limit_operators = $self->option('operators')) {
@operators = split(',', $limit_operators);
sub _create_custom_fields {
my ($self) = @_;
return if !$self->option('add-custom-fields');
-
+
while (my ($type, $name) = each %{ CUSTOM_FIELDS() }) {
my $exists = new Bugzilla::Field({ name => $name });
next if $exists;
sub _create_field_values {
my ($self, $number, $for_create) = @_;
my $dbh = Bugzilla->dbh;
-
+
Bugzilla->set_user($self->admin);
my @selects = grep { $_->is_select } $self->all_fields;
initialqacontact => create_user("$number-defaultqa")->login,
initial_cc => [create_user("$number-initcc")->login],
description => "Component $number" });
-
+
$values{'product'} = $product->name;
$values{'component'} = $component->name;
$values{'target_milestone'} = $milestone;
$values{$field} = $value;
}
$values{'tag'} = ["$number-tag-" . random()];
-
+
my @date_fields = grep { $_->type == FIELD_TYPE_DATETIME } $self->all_fields;
foreach my $field (@date_fields) {
# We use 03 as the month because that differs from our creation_ts,
my $name = "$number-flag-" . random();
my $desc = "FlagType $number";
- my %flagtypes;
+ my %flagtypes;
foreach my $target (qw(a b)) {
$dbh->do("INSERT INTO flagtypes
- (name, description, target_type, is_requestable,
+ (name, description, target_type, is_requestable,
is_requesteeble, is_multiplicable, cc_list)
VALUES (?,?,?,1,1,1,'')",
undef, $name, $desc, $target);
sub _create_one_bug {
my ($self, $number) = @_;
my $dbh = Bugzilla->dbh;
-
+
# We need bug 6 to have a unique alias that is not a clone of bug 1's,
# so we get the alias separately from the other parameters.
my $alias = $self->bug_create_value($number, 'alias');
my $update_alias = $self->bug_update_value($number, 'alias');
-
+
# Otherwise, make bug 6 a clone of bug 1.
my $real_number = $number;
$number = 1 if $number == 6;
-
+
my $reporter = $self->bug_create_value($number, 'reporter');
Bugzilla->set_user(Bugzilla::User->check($reporter));
-
+
# We create the bug with one set of values, and then we change it
# to have different values.
my %params = %{ $self->_bug_create_values->{$number} };
$params{alias} = $alias;
-
+
# There are some things in bug_create_values that shouldn't go into
# create().
delete @params{qw(attachment set_flags tag)};
-
- my ($status, $resolution, $see_also) =
+
+ my ($status, $resolution, $see_also) =
delete @params{qw(bug_status resolution see_also)};
# All the bugs are created with everconfirmed = 0.
$params{bug_status} = 'UNCONFIRMED';
my $bug = Bugzilla::Bug->create(\%params);
-
+
# These are necessary for the changedfrom tests.
my $extra_values = $self->_extra_bug_create_values->{$number};
foreach my $field (qw(comments remaining_time percentage_complete
}
$extra_values->{reporter_accessible} = $number == 1 ? 0 : 1;
$extra_values->{cclist_accessible} = $number == 1 ? 0 : 1;
-
+
if ($number == 5) {
# Bypass Bugzilla::Bug--we don't want any changes in bugs_activity
# for bug 5.
# last comment contains all of the important data, like work_time.
$bug->add_comment("1-comment-" . random(100));
}
-
+
my %update_params = %{ $self->_bug_update_values->{$number} };
my %reverse_map = reverse %{ Bugzilla::Bug->FIELD_MAP };
foreach my $db_name (keys %reverse_map) {
$update_params{$update_name} = delete $update_params{$db_name};
}
}
-
- my ($new_status, $new_res) =
+
+ my ($new_status, $new_res) =
delete @update_params{qw(status resolution)};
# Bypass the status workflow.
$bug->{bug_status} = $new_status;
$bug->{resolution} = $new_res;
$bug->{everconfirmed} = 1 if $number == 1;
-
+
# add/remove/set fields.
$update_params{keywords} = { set => $update_params{keywords} };
$update_params{groups} = { add => $update_params{groups},
$update_params{cc} = { add => \@cc_add, remove => \@cc_remove };
my $see_also_remove = $bug->see_also;
my $see_also_add = [$update_params{see_also}];
- $update_params{see_also} = { add => $see_also_add,
+ $update_params{see_also} = { add => $see_also_add,
remove => $see_also_remove };
$update_params{comment} = { body => $update_params{comment} };
$update_params{work_time} = $number;
$timestamp->set(second => $number);
$bug->update($timestamp->ymd . ' ' . $timestamp->hms);
$extra_values->{flags} = $bug->flags;
-
+
# It's not generally safe to do update() multiple times on
# the same Bug object.
$bug = new Bugzilla::Bug($bug->id);
$bug->set_comment_is_private({ $comment_id => 1 });
}
$bug->update($bug->delta_ts);
-
+
my $attach_create = $self->bug_create_value($number, 'attachment');
my $attachment = Bugzilla::Attachment->create({
bug => $bug,
creation_ts => $creation_ts,
%$attach_create });
# Store for the changedfrom tests.
- $extra_values->{attachments} =
+ $extra_values->{attachments} =
[new Bugzilla::Attachment($attachment->id)];
-
+
my $attach_update = $self->bug_update_value($number, 'attachment');
$attachment->set_all($attach_update);
# In order to keep the mimetype on the ispatch attachment,
$attachment->set_flags([], $attach_flags);
$attachment->update($bug->delta_ts);
}
-
+
# Values for changedfrom.
$extra_values->{creation_ts} = $bug->creation_ts;
$extra_values->{delta_ts} = $bug->creation_ts;
-
+
return new Bugzilla::Bug($bug->id);
}
local $SIG{__DIE__} = \&Carp::confess;
$dbh->bz_start_transaction();
-
+
# Some parameters need to be set in order for the tests to function
# properly.
my $everybody = $self->everybody;
local $params->{'insidergroup'} = $everybody->name;
$self->_setup_bugs();
-
+
# Even though _setup_bugs set us as an admin, we want to be sure at
# this point that we have an admin with refreshed group memberships.
Bugzilla->set_user($self->admin);
sub _setup_dependencies {
my ($self) = @_;
my $dbh = Bugzilla->dbh;
-
+
# Set up depedency relationships between the bugs.
# Bug 1 + 6 depend on bug 2 and block bug 3.
my $bug2 = $self->bug(2);
# searches still work right.
my $set_delta = $dbh->prepare(
'UPDATE bugs SET delta_ts = ? WHERE bug_id = ?');
- foreach my $row ([$original_delta[0], $bug2->id],
+ foreach my $row ([$original_delta[0], $bug2->id],
[$original_delta[1], $bug3->id])
{
$set_delta->execute(@$row);
sub _protect_bug_6 {
my ($self) = @_;
my $dbh = Bugzilla->dbh;
-
+
Bugzilla->set_user($self->admin);
-
+
# Put bug6 in the nobody group.
my $nobody = $self->nobody;
# We pull it newly from the DB to be sure it's safe to call update()
my $bug6 = new Bugzilla::Bug($self->bug(6)->id);
$bug6->add_group($nobody);
$bug6->update($bug6->delta_ts);
-
+
# Remove the admin (and everybody else) from the $nobody group.
- $dbh->do('DELETE FROM group_group_map
+ $dbh->do('DELETE FROM group_group_map
WHERE grantor_id = ? OR member_id = ?', undef,
$nobody->id, $nobody->id);
}
"cc" => { contains => [1] },
"estimated_time" => { contains => [1] },
"flagtypes.name" => { contains => [1] },
- "keywords" => { contains => [1] },
+ "keywords" => { contains => [1] },
FIELD_TYPE_MULTI_SELECT, { contains => [1] },
);
# on Bug 1. If we did an "anywordssubstr" search test, it would
# become a space-separated string of the first few characters
# of each CC's login name on Bug 1.
-#
+#
# <#-id> - The bug id of the numbered bug.
# <#-reporter> - The login name of the numbered bug's reporter.
# <#-delta> - The delta_ts of the numbered bug.
{ contains => [2,3,4,5], value => '^1-', override => REGEX_OVERRIDE },
],
lessthan => [
- { contains => [1], value => 2,
+ { contains => [1], value => 2,
override => {
# A lot of these contain bug 5 because an empty value is validly
# less than the specified value.
{ contains => [2,3,4,5], value => '<1>' },
],
anyexact => [
- { contains => [1,2], value => '<1>, <2>',
+ { contains => [1,2], value => '<1>, <2>',
override => { ANY_OVERRIDE } },
],
anywordssubstr => [
- { contains => [1,2], value => '<1> <2>',
+ { contains => [1,2], value => '<1> <2>',
override => {
ANY_OVERRIDE,
percentage_complete => { contains => [1,2,3] },
# bug 3 has the value "21" here, so matches "2,1"
percentage_complete => { value => '<2>,<3>', contains => [3] },
# 1 0 matches bug 1, which has both public and private comments.
- 'longdescs.isprivate' => { contains => [1] },
+ 'longdescs.isprivate' => { contains => [1] },
}
},
],
override => {
MULTI_BOOLEAN_OVERRIDE,
dependson => { value => '<1> <3>', contains => [1,3] },
- 'longdescs.count' => { contains => [1,2,3,4] },
+ 'longdescs.count' => { contains => [1,2,3,4] },
},
},
],
],
changedafter => [
{ contains => [2,3,4], value => '<2-delta>',
- override => {
+ override => {
CHANGED_OVERRIDE,
creation_ts => { contains => [3,4] },
# We only change this for one bug, and it doesn't match.
contains => [1,2,3,4] },
{ field => 'bug_status', operator => 'anyexact', value => '__all__',
contains => [1,2,3,4,5] },
-
+
{ field => 'resolution', operator => 'anyexact', value => '---',
contains => [5] },
-
+
# email* query parameters.
{ field => 'assigned_to', operator => 'anyexact',
value => '<1>, <2-reporter>', contains => [1,2],
email2 => generate_random_password(100), emaillongdesc2 => 1,
},
},
-
+
# standard pronouns
{ field => 'assigned_to', operator => 'equals', value => '%assignee%',
contains => [1,2,3,4,5] },
my $field = $self->field;
my $operator = $self->operator;
my $value = $self->main_value;
-
+
my $name = "$field-$operator-$value";
if (my $extra_name = $self->test->{extra_name}) {
$name .= "-$extra_name";
sub test {
my $self = shift;
return $self->{test} if $self->{test};
-
+
my %test = %{ $self->{raw_test} };
-
+
# We have field name overrides...
my $override = $test{override}->{$self->field};
# And also field type overrides.
if (!$override) {
$override = $test{override}->{$self->field_object->type} || {};
}
-
+
foreach my $key (%$override) {
$test{$key} = $override->{$key};
}
-
+
$self->{test} = \%test;
return $self->{test};
}
sub _field_values {
my ($self) = @_;
return $self->{field_values} if $self->{field_values};
-
+
my %field_values;
foreach my $number (1..NUM_BUGS) {
$field_values{$number} = $self->_field_values_for_bug($number);
}
return undef;
}
-
+
# Returns a string if we haven't yet implemented the tests for this field,
# but we plan to in the future.
sub field_not_yet_implemented {
my ($self) = @_;
my $field = $self->field;
my $operator = $self->operator;
-
+
if ($field eq 'content' && $operator !~ /matches/) {
return "content field does not support $operator";
}
"type0-0-0" => $self->operator,
"value0-0-0" => $self->translated_value,
);
-
+
$self->{search_params} = \%params;
return $self->{search_params};
}
@values = $self->_values_for($number, 'groups_in', 'name');
}
elsif ($field eq 'keywords') {
- @values = $self->_values_for($number, 'keyword_objects', 'name');
+ @values = $self->_values_for($number, 'keyword_objects', 'name');
}
elsif ($field eq 'content') {
@values = $self->_values_for($number, 'short_desc');
@values = map { blessed($_) ? $_->login : $_ } @values;
@values = grep { defined $_ } @values;
}
-
+
return \@values;
}
sub _translate_value_for_bug {
my ($self, $number, $value) = @_;
-
+
my $bug = $self->bug($number);
-
+
my $bug_id = $bug->id;
$value =~ s/<$number-id>/$bug_id/g;
my $bug_delta = $bug->delta_ts;
my $group = $bug_groups[0];
$value =~ s/<$number-bug_group>/$group/g;
}
-
- my @bug_values = $self->bug_values($number);
+
+ my @bug_values = $self->bug_values($number);
return $value if !@bug_values;
-
+
if ($self->operator =~ /substr/) {
@bug_values = map { $self->_substr_value($_) } @bug_values;
}
$string_value = quotemeta($string_value);
}
$value =~ s/<$number>/$string_value/g;
-
+
return $value;
}
sub run {
my ($self) = @_;
-
+
my $invalid_combination = $self->invalid_field_operator_combination;
my $field_not_implemented = $self->field_not_yet_implemented;
- SKIP: {
+ SKIP: {
skip($invalid_combination, $self->num_tests) if $invalid_combination;
TODO: {
todo_skip ($field_not_implemented, $self->num_tests) if $field_not_implemented;
my $name = $self->name;
my $search_broken = $self->search_known_broken;
-
+
my $search = $self->_test_search_object_creation();
my $sql;
local $TODO = $search_broken if $search_broken;
lives_ok { $sql = $search->_sql } "$name: generate SQL";
}
-
+
my $results;
SKIP: {
skip "Can't run SQL without any SQL", 1 if !defined $sql;
sub _test_content_for_bug {
my ($self, $number, $results, $sql) = @_;
my $name = $self->name;
-
+
my $contains_known_broken = $self->contains_known_broken($number);
-
+
my %result_ids = map { $_->[0] => 1 } @$results;
my $bug_id = $self->bug($number)->id;
-
+
TODO: {
local $TODO = $contains_known_broken if $contains_known_broken;
if ($self->bug_is_contained($number)) {
if ($operator eq 'anyexact') {
$value = [split ',', $value];
}
-
+
if (my $ch_param = CH_OPERATOR->{$operator}) {
if ($field eq 'creation_ts') {
$field = '[Bug creation]';
}
return { chfield => $field, $ch_param => $value };
}
-
+
if ($field eq 'delta_ts' and $operator eq 'greaterthaneq') {
return { chfieldfrom => $value };
}
if ($field eq 'delta_ts' and $operator eq 'lessthaneq') {
return { chfieldto => $value };
}
-
+
if ($field eq 'deadline' and $operator eq 'greaterthaneq') {
return { deadlinefrom => $value };
}
if ($field eq 'deadline' and $operator eq 'lessthaneq') {
return { deadlineto => $value };
}
-
+
if (grep { $_ eq $field } EMAIL_FIELDS) {
$field = 'longdesc' if $field eq 'commenter';
return {
$normal_test->run();
my $not_test = new Bugzilla::Test::Search::NotTest($field_test);
$not_test->run();
-
+
next if !$self->search_test->option('long');
# Run the OR tests. This tests every other operator (including
}
sub search_known_broken {
my ($self) = @_;
- return $self->_join_messages('search_known_broken');
+ return $self->_join_messages('search_known_broken');
}
sub _join_messages {
or ( !$self->bug_is_contained($number)
and $self->_bug_will_actually_be_contained($number) ) )
{
- my @messages = map { $_->contains_known_broken($number) }
+ my @messages = map { $_->contains_known_broken($number) }
$self->field_tests;
@messages = grep { $_ } @messages;
# Sometimes, with things that break because of no_criteria, there won't