Commit a128f843 authored by Christian Legnitto's avatar Christian Legnitto Committed by Max Kanat-Alexander

Bug 590334: Change Bug.pm to use the comment object (Bugzilla::Comment)

when creating or updating bug comment r=mkanat, a=mkanat
parent 2b8ade66
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
# Frédéric Buclin <LpSolit@gmail.com> # Frédéric Buclin <LpSolit@gmail.com>
# Lance Larsh <lance.larsh@oracle.com> # Lance Larsh <lance.larsh@oracle.com>
# Elliotte Martin <elliotte_martin@yahoo.com> # Elliotte Martin <elliotte_martin@yahoo.com>
# Christian Legnitto <clegnitto@mozilla.com>
package Bugzilla::Bug; package Bugzilla::Bug;
...@@ -125,11 +126,11 @@ sub VALIDATORS { ...@@ -125,11 +126,11 @@ sub VALIDATORS {
bug_status => \&_check_bug_status, bug_status => \&_check_bug_status,
cc => \&_check_cc, cc => \&_check_cc,
comment => \&_check_comment, comment => \&_check_comment,
commentprivacy => \&_check_commentprivacy,
component => \&_check_component, component => \&_check_component,
creation_ts => \&_check_creation_ts,
deadline => \&_check_deadline, deadline => \&_check_deadline,
dup_id => \&_check_dup_id, dup_id => \&_check_dup_id,
estimated_time => \&_check_estimated_time, estimated_time => \&Bugzilla::Object::check_time,
everconfirmed => \&Bugzilla::Object::check_boolean, everconfirmed => \&Bugzilla::Object::check_boolean,
groups => \&_check_groups, groups => \&_check_groups,
keywords => \&_check_keywords, keywords => \&_check_keywords,
...@@ -137,7 +138,7 @@ sub VALIDATORS { ...@@ -137,7 +138,7 @@ sub VALIDATORS {
priority => \&_check_priority, priority => \&_check_priority,
product => \&_check_product, product => \&_check_product,
qa_contact => \&_check_qa_contact, qa_contact => \&_check_qa_contact,
remaining_time => \&_check_remaining_time, remaining_time => \&Bugzilla::Object::check_time,
rep_platform => \&_check_select_field, rep_platform => \&_check_select_field,
resolution => \&_check_resolution, resolution => \&_check_resolution,
short_desc => \&_check_short_desc, short_desc => \&_check_short_desc,
...@@ -185,6 +186,7 @@ sub VALIDATOR_DEPENDENCIES { ...@@ -185,6 +186,7 @@ sub VALIDATOR_DEPENDENCIES {
assigned_to => ['component'], assigned_to => ['component'],
bug_status => ['product', 'comment', 'target_milestone'], bug_status => ['product', 'comment', 'target_milestone'],
cc => ['component'], cc => ['component'],
comment => ['creation_ts'],
component => ['product'], component => ['product'],
dup_id => ['bug_status', 'resolution'], dup_id => ['bug_status', 'resolution'],
groups => ['product'], groups => ['product'],
...@@ -246,16 +248,6 @@ sub DATE_COLUMNS { ...@@ -246,16 +248,6 @@ sub DATE_COLUMNS {
return map { $_->name } @fields; return map { $_->name } @fields;
} }
# This is used by add_comment to know what we validate before putting in
# the DB.
use constant UPDATE_COMMENT_COLUMNS => qw(
thetext
work_time
type
extra_data
isprivate
);
# Used in LogActivityEntry(). Gives the max length of lines in the # Used in LogActivityEntry(). Gives the max length of lines in the
# activity table. # activity table.
use constant MAX_LINE_LENGTH => 254; use constant MAX_LINE_LENGTH => 254;
...@@ -292,6 +284,9 @@ use constant REQUIRED_FIELD_MAP => { ...@@ -292,6 +284,9 @@ use constant REQUIRED_FIELD_MAP => {
component_id => 'component', component_id => 'component',
}; };
# Creation timestamp is here because it needs to be validated
# but it can be NULL in the database (see comments in create above)
#
# Target Milestone is here because it has a default that the validator # Target Milestone is here because it has a default that the validator
# creates (product.defaultmilestone) that is different from the database # creates (product.defaultmilestone) that is different from the database
# default. # default.
...@@ -305,7 +300,7 @@ use constant REQUIRED_FIELD_MAP => { ...@@ -305,7 +300,7 @@ use constant REQUIRED_FIELD_MAP => {
# #
# Groups are in a separate table, but must always be validated so that # Groups are in a separate table, but must always be validated so that
# mandatory groups get set on bugs. # mandatory groups get set on bugs.
use constant EXTRA_REQUIRED_FIELDS => qw(target_milestone cc qa_contact groups); use constant EXTRA_REQUIRED_FIELDS => qw(creation_ts target_milestone cc qa_contact groups);
##################################################################### #####################################################################
...@@ -615,14 +610,12 @@ sub create { ...@@ -615,14 +610,12 @@ sub create {
# These are not a fields in the bugs table, so we don't pass them to # These are not a fields in the bugs table, so we don't pass them to
# insert_create_data. # insert_create_data.
my $cc_ids = delete $params->{cc}; my $cc_ids = delete $params->{cc};
my $groups = delete $params->{groups}; my $groups = delete $params->{groups};
my $depends_on = delete $params->{dependson}; my $depends_on = delete $params->{dependson};
my $blocked = delete $params->{blocked}; my $blocked = delete $params->{blocked};
my $keywords = delete $params->{keywords}; my $keywords = delete $params->{keywords};
my ($comment, $privacy) = ($params->{comment}, $params->{commentprivacy}); my $creation_comment = delete $params->{comment};
delete $params->{comment};
delete $params->{commentprivacy};
# We don't want the bug to appear in the system until it's correctly # We don't want the bug to appear in the system until it's correctly
# protected by groups. # protected by groups.
...@@ -686,20 +679,14 @@ sub create { ...@@ -686,20 +679,14 @@ sub create {
} }
} }
# And insert the comment. We always insert a comment on bug creation, # Comment #0 handling...
# We now have a bug id so we can fill this out
$creation_comment->{'bug_id'} = $bug->id;
# Insert the comment. We always insert a comment on bug creation,
# but sometimes it's blank. # but sometimes it's blank.
my @columns = qw(bug_id who bug_when thetext); Bugzilla::Comment->insert_create_data($creation_comment);
my @values = ($bug->bug_id, $bug->{reporter_id}, $timestamp, $comment);
# We don't include the "isprivate" column unless it was specified.
# This allows it to fall back to its database default.
if (defined $privacy) {
push(@columns, 'isprivate');
push(@values, $privacy);
}
my $qmarks = "?," x @columns;
chop($qmarks);
$dbh->do('INSERT INTO longdescs (' . join(',', @columns) . ")
VALUES ($qmarks)", undef, @values);
Bugzilla::Hook::process('bug_end_of_create', { bug => $bug, Bugzilla::Hook::process('bug_end_of_create', { bug => $bug,
timestamp => $timestamp, timestamp => $timestamp,
...@@ -726,8 +713,6 @@ sub run_create_validators { ...@@ -726,8 +713,6 @@ sub run_create_validators {
# Callers cannot set reporter, creation_ts, or delta_ts. # Callers cannot set reporter, creation_ts, or delta_ts.
$params->{reporter} = $class->_check_reporter(); $params->{reporter} = $class->_check_reporter();
$params->{creation_ts} =
Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$params->{delta_ts} = $params->{creation_ts}; $params->{delta_ts} = $params->{creation_ts};
if ($params->{estimated_time}) { if ($params->{estimated_time}) {
...@@ -903,26 +888,21 @@ sub update { ...@@ -903,26 +888,21 @@ sub update {
# Comments # Comments
foreach my $comment (@{$self->{added_comments} || []}) { foreach my $comment (@{$self->{added_comments} || []}) {
my $columns = join(',', keys %$comment); $comment = Bugzilla::Comment->insert_create_data($comment);
my @values = values %$comment;
my $qmarks = join(',', ('?') x @values);
$dbh->do("INSERT INTO longdescs (bug_id, who, bug_when, $columns)
VALUES (?,?,?,$qmarks)", undef,
$self->bug_id, Bugzilla->user->id, $delta_ts, @values);
if ($comment->{work_time}) { if ($comment->{work_time}) {
LogActivityEntry($self->id, "work_time", "", $comment->{work_time}, LogActivityEntry($self->id, "work_time", "", $comment->{work_time},
Bugzilla->user->id, $delta_ts); Bugzilla->user->id, $delta_ts);
} }
} }
# Comment Privacy # Comment Privacy
foreach my $comment_id (keys %{$self->{comment_isprivate} || {}}) { foreach my $comment (@{$self->{comment_isprivate} || []}) {
$dbh->do("UPDATE longdescs SET isprivate = ? WHERE comment_id = ?", $comment->update();
undef, $self->{comment_isprivate}->{$comment_id}, $comment_id);
my ($from, $to) my ($from, $to)
= $self->{comment_isprivate}->{$comment_id} ? (0, 1) : (1, 0); = $comment->is_private ? (0, 1) : (1, 0);
LogActivityEntry($self->id, "longdescs.isprivate", $from, $to, LogActivityEntry($self->id, "longdescs.isprivate", $from, $to,
Bugzilla->user->id, $delta_ts, $comment_id); Bugzilla->user->id, $delta_ts, $comment->id);
} }
# Insert the values into the multiselect value tables # Insert the values into the multiselect value tables
...@@ -1417,33 +1397,43 @@ sub _check_cc { ...@@ -1417,33 +1397,43 @@ sub _check_cc {
} }
sub _check_comment { sub _check_comment {
my ($invocant, $comment) = @_; my ($invocant, $comment_txt, undef, $params) = @_;
$comment = '' unless defined $comment; # Comment can be empty. We should force it to be empty if the text is undef
if (!defined $comment_txt) {
$comment_txt = '';
}
# Remove any trailing whitespace. Leading whitespace could be # Load up some data
# a valid part of the comment. my $isprivate = $params->{commentprivacy};
$comment =~ s/\s*$//s; my $timestamp = $params->{creation_ts};
$comment =~ s/\r\n?/\n/g; # Get rid of \r.
ThrowUserError('comment_too_long') if length($comment) > MAX_COMMENT_LENGTH; # Create the new comment so we can check it
return $comment; my $comment = {
} thetext => $comment_txt,
bug_when => $timestamp,
};
sub _check_commentprivacy { # We don't include the "isprivate" column unless it was specified.
my ($invocant, $comment_privacy) = @_; # This allows it to fall back to its database default.
if ($comment_privacy && !Bugzilla->user->is_insider) { if (defined $isprivate) {
ThrowUserError('user_not_insider'); $comment->{isprivate} = $isprivate;
} }
return $comment_privacy ? 1 : 0;
}
sub _check_comment_type { # Don't need this anymore as it is now in the comment hash
my ($invocant, $type) = @_; delete $params->{commentprivacy};
detaint_natural($type)
|| ThrowCodeError('bad_arg', { argument => 'type', # Validate comment. We have to do this special as a comment normally
function => caller }); # requires a bug to be already created. For a new bug, the first comment
return $type; # obviously can't get the bug if the bug is created after this
# (see bug 590334)
Bugzilla::Comment->check_required_create_fields($comment);
$comment = Bugzilla::Comment->run_create_validators($comment,
{ skip => ['bug_id'] }
);
return $comment;
} }
sub _check_component { sub _check_component {
...@@ -1456,6 +1446,10 @@ sub _check_component { ...@@ -1456,6 +1446,10 @@ sub _check_component {
return $obj; return $obj;
} }
sub _check_creation_ts {
return Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
}
sub _check_deadline { sub _check_deadline {
my ($invocant, $date) = @_; my ($invocant, $date) = @_;
...@@ -1605,10 +1599,6 @@ sub _check_dup_id { ...@@ -1605,10 +1599,6 @@ sub _check_dup_id {
return $dupe_of; return $dupe_of;
} }
sub _check_estimated_time {
return $_[0]->_check_time($_[1], 'estimated_time');
}
sub _check_groups { sub _check_groups {
my ($invocant, $group_names, undef, $params) = @_; my ($invocant, $group_names, undef, $params) = @_;
my $product = blessed($invocant) ? $invocant->product_obj my $product = blessed($invocant) ? $invocant->product_obj
...@@ -1728,10 +1718,6 @@ sub _check_qa_contact { ...@@ -1728,10 +1718,6 @@ sub _check_qa_contact {
return $id || undef; return $id || undef;
} }
sub _check_remaining_time {
return $_[0]->_check_time($_[1], 'remaining_time');
}
sub _check_reporter { sub _check_reporter {
my $invocant = shift; my $invocant = shift;
my $reporter; my $reporter;
...@@ -1886,20 +1872,6 @@ sub _check_target_milestone { ...@@ -1886,20 +1872,6 @@ sub _check_target_milestone {
return $object->name; return $object->name;
} }
sub _check_time {
my ($invocant, $time, $field) = @_;
my $current = 0;
if (ref $invocant && $field ne 'work_time') {
$current = $invocant->$field;
}
return $current unless Bugzilla->user->is_timetracker;
$time = trim($time) || 0;
ValidateTime($time, $field);
return $time;
}
sub _check_version { sub _check_version {
my ($invocant, $version, undef, $params) = @_; my ($invocant, $version, undef, $params) = @_;
$version = trim($version); $version = trim($version);
...@@ -1910,10 +1882,6 @@ sub _check_version { ...@@ -1910,10 +1882,6 @@ sub _check_version {
return $object->name; return $object->name;
} }
sub _check_work_time {
return $_[0]->_check_time($_[1], 'work_time');
}
# Custom Field Validators # Custom Field Validators
sub _check_field_is_mandatory { sub _check_field_is_mandatory {
...@@ -2265,8 +2233,9 @@ sub set_comment_is_private { ...@@ -2265,8 +2233,9 @@ sub set_comment_is_private {
$isprivate = $isprivate ? 1 : 0; $isprivate = $isprivate ? 1 : 0;
if ($isprivate != $comment->is_private) { if ($isprivate != $comment->is_private) {
$self->{comment_isprivate} ||= {}; $self->{comment_isprivate} ||= [];
$self->{comment_isprivate}->{$comment_id} = $isprivate; $comment->set_is_private($isprivate);
push @{$self->{comment_isprivate}}, $comment;
} }
} }
sub set_component { sub set_component {
...@@ -2636,23 +2605,22 @@ sub remove_cc { ...@@ -2636,23 +2605,22 @@ sub remove_cc {
sub add_comment { sub add_comment {
my ($self, $comment, $params) = @_; my ($self, $comment, $params) = @_;
$comment = $self->_check_comment($comment);
$params ||= {}; $params ||= {};
$params->{work_time} = $self->_check_work_time($params->{work_time});
if (exists $params->{type}) {
$params->{type} = $self->_check_comment_type($params->{type});
}
if (exists $params->{isprivate}) {
$params->{isprivate} =
$self->_check_commentprivacy($params->{isprivate});
}
# XXX We really should check extra_data, too.
# This makes it so we won't create new comments when there is nothing
# to add
if ($comment eq '' && !($params->{type} || abs($params->{work_time}))) { if ($comment eq '' && !($params->{type} || abs($params->{work_time}))) {
return; return;
} }
# Fill out info that doesn't change and callers may not pass in
$params->{'bug_id'} = $self;
$params->{'thetext'} = $comment;
# Validate all the entered data
Bugzilla::Comment->check_required_create_fields($params);
$params = Bugzilla::Comment->run_create_validators($params);
# If the user has explicitly set remaining_time, this will be overridden # If the user has explicitly set remaining_time, this will be overridden
# later in set_all. But if they haven't, this keeps remaining_time # later in set_all. But if they haven't, this keeps remaining_time
# up-to-date. # up-to-date.
...@@ -2660,23 +2628,9 @@ sub add_comment { ...@@ -2660,23 +2628,9 @@ sub add_comment {
$self->set_remaining_time(max($self->remaining_time - $params->{work_time}, 0)); $self->set_remaining_time(max($self->remaining_time - $params->{work_time}, 0));
} }
# So we really want to comment. Make sure we are allowed to do so.
my $privs;
$self->check_can_change_field('longdesc', 0, 1, \$privs)
|| ThrowUserError('illegal_change', { field => 'longdesc', privs => $privs });
$self->{added_comments} ||= []; $self->{added_comments} ||= [];
my $add_comment = dclone($params);
$add_comment->{thetext} = $comment;
# We only want to trick_taint fields that we know about--we don't push(@{$self->{added_comments}}, $params);
# want to accidentally let somebody set some field that's not OK
# to set!
foreach my $field (UPDATE_COMMENT_COLUMNS) {
trick_taint($add_comment->{$field}) if defined $add_comment->{$field};
}
push(@{$self->{added_comments}}, $add_comment);
} }
# There was a lot of duplicate code when I wrote this as three separate # There was a lot of duplicate code when I wrote this as three separate
...@@ -3638,29 +3592,6 @@ sub EmitDependList { ...@@ -3638,29 +3592,6 @@ sub EmitDependList {
return $list_ref; return $list_ref;
} }
sub ValidateTime {
my ($time, $field) = @_;
# regexp verifies one or more digits, optionally followed by a period and
# zero or more digits, OR we have a period followed by one or more digits
# (allow negatives, though, so people can back out errors in time reporting)
if ($time !~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/) {
ThrowUserError("number_not_numeric",
{field => "$field", num => "$time"});
}
# Only the "work_time" field is allowed to contain a negative value.
if ( ($time < 0) && ($field ne "work_time") ) {
ThrowUserError("number_too_small",
{field => "$field", num => "$time", min_num => "0"});
}
if ($time > 99999.99) {
ThrowUserError("number_too_large",
{field => "$field", num => "$time", max_num => "99999.99"});
}
}
# Get the activity of a bug, starting from $starttime (if given). # Get the activity of a bug, starting from $starttime (if given).
# This routine assumes Bugzilla::Bug->check has been previously called. # This routine assumes Bugzilla::Bug->check has been previously called.
sub GetBugActivity { sub GetBugActivity {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# All rights reserved. # All rights reserved.
# #
# Contributor(s): James Robson <arbingersys@gmail.com> # Contributor(s): James Robson <arbingersys@gmail.com>
# Christian Legnitto <clegnitto@mozilla.com>
use strict; use strict;
...@@ -50,6 +51,7 @@ use constant DB_COLUMNS => qw( ...@@ -50,6 +51,7 @@ use constant DB_COLUMNS => qw(
); );
use constant UPDATE_COLUMNS => qw( use constant UPDATE_COLUMNS => qw(
isprivate
type type
extra_data extra_data
); );
...@@ -62,12 +64,21 @@ use constant ID_FIELD => 'comment_id'; ...@@ -62,12 +64,21 @@ use constant ID_FIELD => 'comment_id';
use constant LIST_ORDER => 'bug_when, comment_id'; use constant LIST_ORDER => 'bug_when, comment_id';
use constant VALIDATORS => { use constant VALIDATORS => {
extra_data => \&_check_extra_data, bug_id => \&_check_bug_id,
type => \&_check_type, who => \&_check_who,
bug_when => \&_check_bug_when,
work_time => \&_check_work_time,
thetext => \&_check_thetext,
isprivate => \&_check_isprivate,
extra_data => \&_check_extra_data,
type => \&_check_type,
}; };
use constant VALIDATOR_DEPENDENCIES => { use constant VALIDATOR_DEPENDENCIES => {
extra_data => ['type'], extra_data => ['type'],
bug_id => ['who'],
work_time => ['who'],
isprivate => ['who'],
}; };
######################### #########################
...@@ -157,12 +168,9 @@ sub body_full { ...@@ -157,12 +168,9 @@ sub body_full {
# Mutators # # Mutators #
############ ############
sub set_extra_data { $_[0]->set('extra_data', $_[1]); } sub set_is_private { $_[0]->set('isprivate', $_[1]); }
sub set_type { $_[0]->set('type', $_[1]); }
sub set_type { sub set_extra_data { $_[0]->set('extra_data', $_[1]); }
my ($self, $type) = @_;
$self->set('type', $type);
}
############## ##############
# Validators # # Validators #
...@@ -209,6 +217,87 @@ sub _check_type { ...@@ -209,6 +217,87 @@ sub _check_type {
return $type; return $type;
} }
sub _check_bug_id {
my ($invocant, $bug_id) = @_;
ThrowCodeError('param_required', {function => 'Bugzilla::Comment->create',
param => 'bug_id'}) unless $bug_id;
my $bug;
if (blessed $bug_id) {
# We got a bug object passed in, use it
$bug = $bug_id;
$bug->check_is_visible;
}
else {
# We got a bug id passed in, check it and get the bug object
$bug = Bugzilla::Bug->check({ id => $bug_id });
}
# Make sure the user can edit the product
Bugzilla->user->can_edit_product($bug->{product_id});
# Make sure the user can comment
my $privs;
$bug->check_can_change_field('longdesc', 0, 1, \$privs)
|| ThrowUserError('illegal_change',
{ field => 'longdesc', privs => $privs });
return $bug->id;
}
sub _check_who {
my ($invocant, $who) = @_;
Bugzilla->login(LOGIN_REQUIRED);
return Bugzilla->user->id;
}
sub _check_bug_when {
my ($invocant, $when) = @_;
# Make sure the timestamp is defined, default to a timestamp from the db
if (!defined $when) {
$when = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
}
# Make sure the timestamp parses
if (!datetime_from($when)) {
ThrowCodeError('invalid_timestamp', { timestamp => $when });
}
return $when;
}
sub _check_work_time {
my ($invocant, $value_in, $field, $params) = @_;
# Call down to Bugzilla::Object, letting it know negative
# values are ok
return $invocant->check_time( $value_in, $field, $params, 1);
}
sub _check_thetext {
my ($invocant, $thetext) = @_;
ThrowCodeError('param_required',{function => 'Bugzilla::Comment->create',
param => 'thetext'}) unless defined $thetext;
# Remove any trailing whitespace. Leading whitespace could be
# a valid part of the comment.
$thetext =~ s/\s*$//s;
$thetext =~ s/\r\n?/\n/g; # Get rid of \r.
ThrowUserError('comment_too_long') if length($thetext) > MAX_COMMENT_LENGTH;
return $thetext;
}
sub _check_isprivate {
my ($invocant, $isprivate) = @_;
if ($isprivate && !Bugzilla->user->is_insider) {
ThrowUserError('user_not_insider');
}
return $isprivate ? 1 : 0;
}
sub count { sub count {
my ($self) = @_; my ($self) = @_;
......
...@@ -30,6 +30,7 @@ use Bugzilla::Error; ...@@ -30,6 +30,7 @@ use Bugzilla::Error;
use Date::Parse; use Date::Parse;
use List::MoreUtils qw(part); use List::MoreUtils qw(part);
use Scalar::Util qw(blessed);
use constant NAME_FIELD => 'name'; use constant NAME_FIELD => 'name';
use constant ID_FIELD => 'id'; use constant ID_FIELD => 'id';
...@@ -462,13 +463,21 @@ sub check_required_create_fields { ...@@ -462,13 +463,21 @@ sub check_required_create_fields {
} }
sub run_create_validators { sub run_create_validators {
my ($class, $params) = @_; my ($class, $params, $options) = @_;
my $validators = $class->_get_validators; my $validators = $class->_get_validators;
my %field_values = %$params; my %field_values = %$params;
# Make a hash skiplist for easier searching later
my %skip_list = map { $_ => 1 } @{ $options->{skip} || [] };
# Get the sorted field names
my @sorted_names = $class->_sort_by_dep(keys %field_values); my @sorted_names = $class->_sort_by_dep(keys %field_values);
foreach my $field (@sorted_names) {
# Remove the skipped names
my @unskipped = grep { !$skip_list{$_} } @sorted_names;
foreach my $field (@unskipped) {
my $value; my $value;
if (exists $validators->{$field}) { if (exists $validators->{$field}) {
my $validator = $validators->{$field}; my $validator = $validators->{$field};
...@@ -527,10 +536,54 @@ sub get_all { ...@@ -527,10 +536,54 @@ sub get_all {
sub check_boolean { return $_[1] ? 1 : 0 } sub check_boolean { return $_[1] ? 1 : 0 }
sub check_time {
my ($invocant, $value, $field, $params, $allow_negative) = @_;
# If we don't have a current value default to zero
my $current = blessed($invocant) ? $invocant->{$field}
: 0;
$current ||= 0;
# Don't let the user set the value if they aren't a timetracker
return $current unless Bugzilla->user->is_timetracker;
# Get the new value or zero if it isn't defined
$value = trim($value) || 0;
# Make sure the new value is well formed
_validate_time($value, $field, $allow_negative);
return $value;
}
################### ###################
# General Helpers # # General Helpers #
################### ###################
sub _validate_time {
my ($time, $field, $allow_negative) = @_;
# regexp verifies one or more digits, optionally followed by a period and
# zero or more digits, OR we have a period followed by one or more digits
# (allow negatives, though, so people can back out errors in time reporting)
if ($time !~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/) {
ThrowUserError("number_not_numeric",
{field => $field, num => "$time"});
}
# Callers can optionally allow negative times
if ( ($time < 0) && !$allow_negative ) {
ThrowUserError("number_too_small",
{field => $field, num => "$time", min_num => "0"});
}
if ($time > 99999.99) {
ThrowUserError("number_too_large",
{field => $field, num => "$time", max_num => "99999.99"});
}
}
# Sorts fields according to VALIDATOR_DEPENDENCIES. This is not a # Sorts fields according to VALIDATOR_DEPENDENCIES. This is not a
# traditional topological sort, because a "dependency" does not # traditional topological sort, because a "dependency" does not
# *have* to be in the list--it just has to be earlier than its dependent # *have* to be in the list--it just has to be earlier than its dependent
...@@ -1036,7 +1089,11 @@ Description: Runs the validation of input parameters for L</create>. ...@@ -1036,7 +1089,11 @@ Description: Runs the validation of input parameters for L</create>.
of their input parameters. This method is B<only> called of their input parameters. This method is B<only> called
by L</create>. by L</create>.
Params: The same as L</create>. Params: C<$params> - hashref - A value to put in each database
field for this object.
C<$options> - hashref - Processing options. Currently
the only option supported is B<skip>, which can be
used to specify a list of fields to not validate.
Returns: A hash, in a similar format as C<$params>, except that Returns: A hash, in a similar format as C<$params>, except that
these are the values to be inserted into the database, these are the values to be inserted into the database,
......
...@@ -70,6 +70,7 @@ use lib qw(. lib); ...@@ -70,6 +70,7 @@ use lib qw(. lib);
use Bugzilla; use Bugzilla;
use Bugzilla::Object;
use Bugzilla::Bug; use Bugzilla::Bug;
use Bugzilla::Product; use Bugzilla::Product;
use Bugzilla::Version; use Bugzilla::Version;
...@@ -763,7 +764,7 @@ sub process_bug { ...@@ -763,7 +764,7 @@ sub process_bug {
push( @query, "deadline" ); push( @query, "deadline" );
if ( defined $bug_fields{'estimated_time'} ) { if ( defined $bug_fields{'estimated_time'} ) {
eval { eval {
Bugzilla::Bug::ValidateTime($bug_fields{'estimated_time'}, "e"); Bugzilla::Object::_validate_time($bug_fields{'estimated_time'}, "e");
}; };
if (!$@){ if (!$@){
push( @values, $bug_fields{'estimated_time'} ); push( @values, $bug_fields{'estimated_time'} );
...@@ -772,7 +773,7 @@ sub process_bug { ...@@ -772,7 +773,7 @@ sub process_bug {
} }
if ( defined $bug_fields{'remaining_time'} ) { if ( defined $bug_fields{'remaining_time'} ) {
eval { eval {
Bugzilla::Bug::ValidateTime($bug_fields{'remaining_time'}, "r"); Bugzilla::Object::_validate_time($bug_fields{'remaining_time'}, "r");
}; };
if (!$@){ if (!$@){
push( @values, $bug_fields{'remaining_time'} ); push( @values, $bug_fields{'remaining_time'} );
...@@ -781,7 +782,7 @@ sub process_bug { ...@@ -781,7 +782,7 @@ sub process_bug {
} }
if ( defined $bug_fields{'actual_time'} ) { if ( defined $bug_fields{'actual_time'} ) {
eval { eval {
Bugzilla::Bug::ValidateTime($bug_fields{'actual_time'}, "a"); Bugzilla::Object::_validate_time($bug_fields{'actual_time'}, "a");
}; };
if ($@){ if ($@){
$bug_fields{'actual_time'} = 0.0; $bug_fields{'actual_time'} = 0.0;
......
...@@ -236,6 +236,10 @@ ...@@ -236,6 +236,10 @@
The series_id [% series_id FILTER html %] is not valid. It may be that The series_id [% series_id FILTER html %] is not valid. It may be that
this series has been deleted. this series has been deleted.
[% ELSIF error == "invalid_timestamp" %]
The entered timestamp <code>[% timestamp FILTER html %]</code> could not
be parsed into a valid date and time.
[% ELSIF error == "invalid_webservergroup" %] [% ELSIF error == "invalid_webservergroup" %]
There is no such group: [% group FILTER html %]. Check your $webservergroup There is no such group: [% group FILTER html %]. Check your $webservergroup
setting in [% constants.bz_locations.localconfig FILTER html %]. setting in [% constants.bz_locations.localconfig FILTER html %].
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment