Commit 5d516a6a authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 472217: Create a Bugzilla::Comment object and eliminate GetComments

Patch by Max Kanat-Alexander <mkanat@bugzilla.org> r=LpSolit, a=LpSolit
parent 88a69f20
...@@ -46,6 +46,7 @@ use Bugzilla::Product; ...@@ -46,6 +46,7 @@ use Bugzilla::Product;
use Bugzilla::Component; use Bugzilla::Component;
use Bugzilla::Group; use Bugzilla::Group;
use Bugzilla::Status; use Bugzilla::Status;
use Bugzilla::Comment;
use List::Util qw(min); use List::Util qw(min);
use Storable qw(dclone); use Storable qw(dclone);
...@@ -1835,12 +1836,12 @@ sub set_cclist_accessible { $_[0]->set('cclist_accessible', $_[1]); } ...@@ -1835,12 +1836,12 @@ sub set_cclist_accessible { $_[0]->set('cclist_accessible', $_[1]); }
sub set_comment_is_private { sub set_comment_is_private {
my ($self, $comment_id, $isprivate) = @_; my ($self, $comment_id, $isprivate) = @_;
return unless Bugzilla->user->is_insider; return unless Bugzilla->user->is_insider;
my ($comment) = grep($comment_id eq $_->{id}, @{$self->longdescs}); my ($comment) = grep($comment_id == $_->id, @{ $self->comments });
ThrowUserError('comment_invalid_isprivate', { id => $comment_id }) ThrowUserError('comment_invalid_isprivate', { id => $comment_id })
if !$comment; if !$comment;
$isprivate = $isprivate ? 1 : 0; $isprivate = $isprivate ? 1 : 0;
if ($isprivate != $comment->{isprivate}) { if ($isprivate != $comment->is_private) {
$self->{comment_isprivate} ||= {}; $self->{comment_isprivate} ||= {};
$self->{comment_isprivate}->{$comment_id} = $isprivate; $self->{comment_isprivate}->{$comment_id} = $isprivate;
} }
...@@ -2665,12 +2666,38 @@ sub keyword_objects { ...@@ -2665,12 +2666,38 @@ sub keyword_objects {
return $self->{'keyword_objects'}; return $self->{'keyword_objects'};
} }
sub longdescs { sub comments {
my ($self) = @_; my ($self, $params) = @_;
return $self->{'longdescs'} if exists $self->{'longdescs'};
return [] if $self->{'error'}; return [] if $self->{'error'};
$self->{'longdescs'} = GetComments($self->{bug_id}); $params ||= {};
return $self->{'longdescs'};
if (!defined $self->{'comments'}) {
$self->{'comments'} = Bugzilla::Comment->match({ bug_id => $self->id });
my $count = 0;
foreach my $comment (@{ $self->{'comments'} }) {
$comment->{count} = $count++;
}
}
my @comments = @{ $self->{'comments'} };
my $order = $params->{order}
|| Bugzilla->user->settings->{'comment_sort_order'}->{'value'};
if ($order ne 'oldest_to_newest') {
@comments = reverse @comments;
if ($order eq 'newest_to_oldest_desc_first') {
unshift(@comments, pop @comments);
}
}
if ($params->{after}) {
my $from = datetime_from($params->{after});
@comments = grep { datetime_from($_->creation_ts) > $from } @comments;
}
if ($params->{to}) {
my $to = datetime_from($params->{to});
@comments = grep { datetime_from($_->creation_ts) <= $to } @comments;
}
return \@comments;
} }
sub milestoneurl { sub milestoneurl {
...@@ -2955,7 +2982,7 @@ sub update_comment { ...@@ -2955,7 +2982,7 @@ sub update_comment {
|| ThrowCodeError('bad_arg', {argument => 'comment_id', function => 'update_comment'}); || ThrowCodeError('bad_arg', {argument => 'comment_id', function => 'update_comment'});
# The comment ID must belong to this bug. # The comment ID must belong to this bug.
my @current_comment_obj = grep {$_->{'id'} == $comment_id} @{$self->longdescs}; my @current_comment_obj = grep {$_->id == $comment_id} @{$self->comments};
scalar(@current_comment_obj) scalar(@current_comment_obj)
|| ThrowCodeError('bad_arg', {argument => 'comment_id', function => 'update_comment'}); || ThrowCodeError('bad_arg', {argument => 'comment_id', function => 'update_comment'});
...@@ -2972,7 +2999,7 @@ sub update_comment { ...@@ -2972,7 +2999,7 @@ sub update_comment {
$self->_sync_fulltext(); $self->_sync_fulltext();
# Update the comment object with this new text. # Update the comment object with this new text.
$current_comment_obj[0]->{'body'} = $new_comment; $current_comment_obj[0]->{'thetext'} = $new_comment;
} }
# Represents which fields from the bugs table are handled by process_bug.cgi. # Represents which fields from the bugs table are handled by process_bug.cgi.
...@@ -3032,74 +3059,6 @@ sub ValidateTime { ...@@ -3032,74 +3059,6 @@ sub ValidateTime {
} }
} }
sub GetComments {
my ($id, $comment_sort_order, $start, $end, $raw) = @_;
my $dbh = Bugzilla->dbh;
$comment_sort_order = $comment_sort_order ||
Bugzilla->user->settings->{'comment_sort_order'}->{'value'};
my $sort_order = ($comment_sort_order eq "oldest_to_newest") ? 'asc' : 'desc';
my @comments;
my @args = ($id);
my $query = 'SELECT longdescs.comment_id AS id, profiles.userid, ' .
$dbh->sql_date_format('longdescs.bug_when', '%Y.%m.%d %H:%i:%s') .
' AS time, longdescs.thetext AS body, longdescs.work_time,
isprivate, already_wrapped, type, extra_data
FROM longdescs
INNER JOIN profiles
ON profiles.userid = longdescs.who
WHERE longdescs.bug_id = ?';
if ($start) {
$query .= ' AND longdescs.bug_when > ?';
push(@args, $start);
}
if ($end) {
$query .= ' AND longdescs.bug_when <= ?';
push(@args, $end);
}
$query .= " ORDER BY longdescs.bug_when $sort_order";
my $sth = $dbh->prepare($query);
$sth->execute(@args);
# Cache the users we look up
my %users;
while (my $comment_ref = $sth->fetchrow_hashref()) {
my %comment = %$comment_ref;
$users{$comment{'userid'}} ||= new Bugzilla::User($comment{'userid'});
$comment{'author'} = $users{$comment{'userid'}};
# If raw data is requested, do not format 'special' comments.
$comment{'body'} = format_comment(\%comment) unless $raw;
push (@comments, \%comment);
}
if ($comment_sort_order eq "newest_to_oldest_desc_first") {
unshift(@comments, pop @comments);
}
return \@comments;
}
# Format language specific comments.
sub format_comment {
my $comment = shift;
my $template = Bugzilla->template_inner;
my $vars = {comment => $comment};
my $body;
$template->process("bug/format_comment.txt.tmpl", $vars, \$body)
|| ThrowTemplateError($template->error());
$body =~ s/^X//;
return $body;
}
# 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 {
...@@ -3644,7 +3603,7 @@ sub _validate_attribute { ...@@ -3644,7 +3603,7 @@ sub _validate_attribute {
my @valid_attributes = ( my @valid_attributes = (
# Miscellaneous properties and methods. # Miscellaneous properties and methods.
qw(error groups product_id component_id qw(error groups product_id component_id
longdescs milestoneurl attachments comments milestoneurl attachments
isopened isunconfirmed isopened isunconfirmed
flag_types num_attachment_flag_types flag_types num_attachment_flag_types
show_attachment_flags any_flags_requesteeble), show_attachment_flags any_flags_requesteeble),
......
...@@ -119,6 +119,7 @@ sub Send { ...@@ -119,6 +119,7 @@ sub Send {
my $msg = ""; my $msg = "";
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $bug = new Bugzilla::Bug($id);
# XXX - These variables below are useless. We could use field object # XXX - These variables below are useless. We could use field object
# methods directly. But we first have to implement a cache in # methods directly. But we first have to implement a cache in
...@@ -356,7 +357,7 @@ sub Send { ...@@ -356,7 +357,7 @@ sub Send {
} }
} }
my $comments = get_comments_by_bug($id, $start, $end); my $comments = $bug->comments({ after => $start, to => $end });
########################################################################### ###########################################################################
# Start of email filtering code # Start of email filtering code
...@@ -569,7 +570,7 @@ sub sendMail { ...@@ -569,7 +570,7 @@ sub sendMail {
} }
if (!$user->is_insider) { if (!$user->is_insider) {
@send_comments = grep { !$_->{isprivate} } @send_comments; @send_comments = grep { !$_->is_private } @send_comments;
} }
if ($difftext eq "" && !scalar(@send_comments) && !$isnew) { if ($difftext eq "" && !scalar(@send_comments) && !$isnew) {
...@@ -650,38 +651,4 @@ sub sendMail { ...@@ -650,38 +651,4 @@ sub sendMail {
return 1; return 1;
} }
# Get bug comments for the given period.
sub get_comments_by_bug {
my ($id, $start, $end) = @_;
my $dbh = Bugzilla->dbh;
my $result = "";
my $count = 0;
# $start will be undef for new bugs, and defined for pre-existing bugs.
if ($start) {
# If $start is not NULL, obtain the count-index
# of this comment for the leading "Comment #xxx" line.
$count = $dbh->selectrow_array('SELECT COUNT(*) FROM longdescs
WHERE bug_id = ? AND bug_when <= ?',
undef, ($id, $start));
}
my $raw = 1; # Do not format comments which are not of type CMT_NORMAL.
my $comments = Bugzilla::Bug::GetComments($id, "oldest_to_newest", $start, $end, $raw);
my $attach_base = correct_urlbase() . 'attachment.cgi?id=';
foreach my $comment (@$comments) {
$comment->{count} = $count++;
# If an attachment was created, then add an URL. (Note: the 'g'lobal
# replace should work with comments with multiple attachments.)
if ($comment->{body} =~ /Created an attachment \(/) {
$comment->{body} =~ s/(Created an attachment \(id=([0-9]+)\))/$1\n --> \($attach_base$2\)/g;
}
$comment->{body} = $comment->{'already_wrapped'} ? $comment->{body} : wrap_comment($comment->{body});
}
return $comments;
}
1; 1;
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is James Robson.
# Portions created by James Robson are Copyright (c) 2009 James Robson.
# All rights reserved.
#
# Contributor(s): James Robson <arbingersys@gmail.com>
use strict;
package Bugzilla::Comment;
use base qw(Bugzilla::Object);
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Util;
###############################
#### Initialization ####
###############################
use constant DB_COLUMNS => qw(
comment_id
bug_id
who
bug_when
work_time
thetext
isprivate
already_wrapped
type
extra_data
);
use constant DB_TABLE => 'longdescs';
use constant ID_FIELD => 'comment_id';
use constant LIST_ORDER => 'bug_when';
###############################
#### Accessors ######
###############################
sub already_wrapped { return $_[0]->{'already_wrapped'}; }
sub body { return $_[0]->{'thetext'}; }
sub bug_id { return $_[0]->{'bug_id'}; }
sub creation_ts { return $_[0]->{'bug_when'}; }
sub is_private { return $_[0]->{'isprivate'}; }
sub work_time { return $_[0]->{'work_time'}; }
sub author {
my $self = shift;
$self->{'author'} ||= new Bugzilla::User($self->{'who'});
return $self->{'author'};
}
sub body_full {
my ($self, $params) = @_;
$params ||= {};
my $template = Bugzilla->template_inner;
my $body;
$template->process("bug/format_comment.txt.tmpl",
{ comment => $self, %$params }, \$body)
|| ThrowTemplateError($template->error());
$body =~ s/^X//;
if ($params->{wrap} and !$self->already_wrapped) {
$body = wrap_comment($body);
}
return $body;
}
1;
__END__
=head1 NAME
Bugzilla::Comment - A Comment for a given bug
=head1 SYNOPSIS
use Bugzilla::Comment;
my $comment = Bugzilla::Comment->new($comment_id);
my $comments = Bugzilla::Comment->new_from_list($comment_ids);
=head1 DESCRIPTION
Bugzilla::Comment represents a comment attached to a bug.
This implements all standard C<Bugzilla::Object> methods. See
L<Bugzilla::Object> for more details.
=head2 Accessors
=over
=item C<bug_id>
C<int> The ID of the bug to which the comment belongs.
=item C<creation_ts>
C<string> The comment creation timestamp.
=item C<body>
C<string> The body without any special additional text.
=item C<work_time>
C<string> Time spent as related to this comment.
=item C<is_private>
C<boolean> Comment is marked as private
=item C<already_wrapped>
If this comment is stored in the database word-wrapped, this will be C<1>.
C<0> otherwise.
=item C<author>
L<Bugzilla::User> who created the comment.
=item C<body_full>
=over
=item B<Description>
C<string> Body of the comment, including any special text (such as
"this bug was marked as a duplicate of...").
=item B<Params>
=over
=item C<is_bugmail>
C<boolean>. C<1> if this comment should be formatted specifically for
bugmail.
=item C<wrap>
C<boolean>. C<1> if the comment should be returned word-wrapped.
=back
=item B<Returns>
A string, the full text of the comment as it would be displayed to an end-user.
=back
=back
=cut
...@@ -170,7 +170,7 @@ sub quoteUrls { ...@@ -170,7 +170,7 @@ sub quoteUrls {
# If the comment is already wrapped, we should ignore newlines when # If the comment is already wrapped, we should ignore newlines when
# looking for matching regexps. Else we should take them into account. # looking for matching regexps. Else we should take them into account.
my $s = ($comment && $comment->{already_wrapped}) my $s = ($comment && $comment->already_wrapped)
? qr/\s/ : qr/[[:blank:]]/; ? qr/\s/ : qr/[[:blank:]]/;
# However, note that adding the title (for buglinks) can affect things # However, note that adding the title (for buglinks) can affect things
......
...@@ -1390,7 +1390,7 @@ sub wants_bug_mail { ...@@ -1390,7 +1390,7 @@ sub wants_bug_mail {
my $self = shift; my $self = shift;
my ($bug_id, $relationship, $fieldDiffs, $comments, $dependencyText, my ($bug_id, $relationship, $fieldDiffs, $comments, $dependencyText,
$changer, $bug_is_new) = @_; $changer, $bug_is_new) = @_;
my $comments_concatenated = join("\n", map { $_->{body} } (@$comments)); my $comments_concatenated = join("\n", map { $_->body } @$comments);
# Make a list of the events which have happened during this bug change, # Make a list of the events which have happened during this bug change,
# from the point of view of this user. # from the point of view of this user.
......
...@@ -40,7 +40,7 @@ use base qw(Exporter); ...@@ -40,7 +40,7 @@ use base qw(Exporter);
diff_arrays diff_arrays
trim wrap_hard wrap_comment find_wrap_point trim wrap_hard wrap_comment find_wrap_point
format_time format_time_decimal validate_date format_time format_time_decimal validate_date
validate_time validate_time datetime_from
file_mod_time is_7bit_clean file_mod_time is_7bit_clean
bz_crypt generate_random_password bz_crypt generate_random_password
validate_email_syntax clean_text validate_email_syntax clean_text
...@@ -396,7 +396,9 @@ sub format_time { ...@@ -396,7 +396,9 @@ sub format_time {
# If $format is undefined, try to guess the correct date format. # If $format is undefined, try to guess the correct date format.
if (!defined($format)) { if (!defined($format)) {
if ($date =~ m/^(\d{4})[-\.](\d{2})[-\.](\d{2}) (\d{2}):(\d{2})(:(\d{2}))?$/) { if (!ref $date
&& $date =~ /^(\d{4})[-\.](\d{2})[-\.](\d{2}) (\d{2}):(\d{2})(:(\d{2}))?$/)
{
my $sec = $7; my $sec = $7;
if (defined $sec) { if (defined $sec) {
$format = "%Y-%m-%d %T %Z"; $format = "%Y-%m-%d %T %Z";
...@@ -409,44 +411,49 @@ sub format_time { ...@@ -409,44 +411,49 @@ sub format_time {
} }
} }
# strptime($date) returns an empty array if $date has an invalid date format. my $dt = ref $date ? $date : datetime_from($date, $timezone);
$date = defined $dt ? $dt->strftime($format) : '';
return trim($date);
}
sub datetime_from {
my ($date, $timezone) = @_;
# strptime($date) returns an empty array if $date has an invalid
# date format.
my @time = strptime($date); my @time = strptime($date);
unless (scalar @time) { unless (scalar @time) {
# If an unknown timezone is passed (such as MSK, for Moskow), strptime() is # If an unknown timezone is passed (such as MSK, for Moskow),
# unable to parse the date. We try again, but we first remove the timezone. # strptime() is unable to parse the date. We try again, but we first
# remove the timezone.
$date =~ s/\s+\S+$//; $date =~ s/\s+\S+$//;
@time = strptime($date); @time = strptime($date);
} }
if (scalar @time) { return undef if !@time;
# Fix a bug in strptime() where seconds can be undefined in some cases.
$time[0] ||= 0; # strptime() counts years from 1900, and months from 0 (January).
# We have to fix both values.
# strptime() counts years from 1900, and months from 0 (January). my $dt = DateTime->new({
# We have to fix both values. year => $time[5] + 1900,
my $dt = DateTime->new({year => 1900 + $time[5], month => $time[4] + 1,
month => ++$time[4], day => $time[3],
day => $time[3], hour => $time[2],
hour => $time[2], minute => $time[1],
minute => $time[1], # DateTime doesn't like fractional seconds.
# DateTime doesn't like fractional seconds. # Also, sometimes seconds are undef.
second => int($time[0]), second => int($time[0] || 0),
# If importing, use the specified timezone, otherwise # If a timezone was specified, use it. Otherwise, use the
# use the timezone specified by the server. # 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}); || Bugzilla->local_timezone,
});
# Now display the date using the given timezone,
# or the user's timezone if none is given. # Now display the date using the given timezone,
$dt->set_time_zone($timezone || Bugzilla->user->timezone); # or the user's timezone if none is given.
$date = $dt->strftime($format); $dt->set_time_zone($timezone || Bugzilla->user->timezone);
} return $dt;
else {
# Don't let invalid (time) strings to be passed to templates!
$date = '';
}
return trim($date);
} }
sub format_time_decimal { sub format_time_decimal {
...@@ -641,6 +648,7 @@ Bugzilla::Util - Generic utility functions for bugzilla ...@@ -641,6 +648,7 @@ Bugzilla::Util - Generic utility functions for bugzilla
# Functions for formatting time # Functions for formatting time
format_time($time); format_time($time);
datetime_from($time, $timezone);
# Functions for dealing with files # Functions for dealing with files
$time = file_mod_time($filename); $time = file_mod_time($filename);
...@@ -894,6 +902,15 @@ This routine is mainly called from templates to filter dates, see ...@@ -894,6 +902,15 @@ This routine is mainly called from templates to filter dates, see
Returns a number with 2 digit precision, unless the last digit is a 0. Then it Returns a number with 2 digit precision, unless the last digit is a 0. Then it
returns only 1 digit precision. returns only 1 digit precision.
=item C<datetime_from($time, $timezone)>
Returns a DateTime object given a date string. If the string is not in some
valid date format that C<strptime> understands, we return C<undef>.
You can optionally specify a timezone for the returned date. If not
specified, defaults to the currently-logged-in user's timezone, or
the Bugzilla server's local timezone if there isn't a logged-in user.
=back =back
......
...@@ -23,6 +23,7 @@ package Bugzilla::WebService::Bug; ...@@ -23,6 +23,7 @@ package Bugzilla::WebService::Bug;
use strict; use strict;
use base qw(Bugzilla::WebService); use base qw(Bugzilla::WebService);
use Bugzilla::Comment;
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Field; use Bugzilla::Field;
...@@ -95,12 +96,12 @@ sub comments { ...@@ -95,12 +96,12 @@ sub comments {
foreach my $bug_id (@$bug_ids) { foreach my $bug_id (@$bug_ids) {
my $bug = Bugzilla::Bug->check($bug_id); my $bug = Bugzilla::Bug->check($bug_id);
# We want the API to always return comments in the same order. # We want the API to always return comments in the same order.
my $comments = Bugzilla::Bug::GetComments(
$bug->id, 'oldest_to_newest', $params->{new_since}); my $comments = $bug->comments({ order => 'oldest_to_newest',
after => $params->{new_since} });
my @result; my @result;
foreach my $comment (@$comments) { foreach my $comment (@$comments) {
next if $comment->{isprivate} && !$user->is_insider; next if $comment->is_private && !$user->is_insider;
$comment->{bug_id} = $bug->id;
push(@result, $self->_translate_comment($comment, $params)); push(@result, $self->_translate_comment($comment, $params));
} }
$bugs{$bug->id}{'comments'} = \@result; $bugs{$bug->id}{'comments'} = \@result;
...@@ -109,15 +110,10 @@ sub comments { ...@@ -109,15 +110,10 @@ sub comments {
my %comments; my %comments;
if (scalar @$comment_ids) { if (scalar @$comment_ids) {
my @ids = map { trim($_) } @$comment_ids; my @ids = map { trim($_) } @$comment_ids;
my @sql_ids = map { $dbh->quote($_) } @ids; my $comment_data = Bugzilla::Comment->new_from_list(\@ids);
my $comment_data = $dbh->selectall_arrayref(
'SELECT comment_id AS id, bug_id, who, bug_when AS time,
isprivate, thetext AS body, type, extra_data
FROM longdescs WHERE ' . $dbh->sql_in('comment_id', \@sql_ids),
{Slice=>{}});
# See if we were passed any invalid comment ids. # See if we were passed any invalid comment ids.
my %got_ids = map { $_->{id} => 1 } @$comment_data; my %got_ids = map { $_->id => 1 } @$comment_data;
foreach my $comment_id (@ids) { foreach my $comment_id (@ids) {
if (!$got_ids{$comment_id}) { if (!$got_ids{$comment_id}) {
ThrowUserError('comment_id_invalid', { id => $comment_id }); ThrowUserError('comment_id_invalid', { id => $comment_id });
...@@ -125,16 +121,14 @@ sub comments { ...@@ -125,16 +121,14 @@ sub comments {
} }
# Now make sure that we can see all the associated bugs. # Now make sure that we can see all the associated bugs.
my %got_bug_ids = map { $_->{bug_id} => 1 } @$comment_data; my %got_bug_ids = map { $_->bug_id => 1 } @$comment_data;
Bugzilla::Bug->check($_) foreach (keys %got_bug_ids); Bugzilla::Bug->check($_) foreach (keys %got_bug_ids);
foreach my $comment (@$comment_data) { foreach my $comment (@$comment_data) {
if ($comment->{isprivate} && !$user->is_insider) { if ($comment->is_private && !$user->is_insider) {
ThrowUserError('comment_is_private', { id => $comment->{id} }); ThrowUserError('comment_is_private', { id => $comment->id });
} }
$comment->{author} = new Bugzilla::User($comment->{who}); $comments{$comment->id} =
$comment->{body} = Bugzilla::Bug::format_comment($comment);
$comments{$comment->{id}} =
$self->_translate_comment($comment, $params); $self->_translate_comment($comment, $params);
} }
} }
...@@ -146,12 +140,12 @@ sub comments { ...@@ -146,12 +140,12 @@ sub comments {
sub _translate_comment { sub _translate_comment {
my ($self, $comment, $filters) = @_; my ($self, $comment, $filters) = @_;
return filter $filters, { return filter $filters, {
id => $self->type('int', $comment->{id}), id => $self->type('int', $comment->id),
bug_id => $self->type('int', $comment->{bug_id}), bug_id => $self->type('int', $comment->bug_id),
author => $self->type('string', $comment->{author}->login), author => $self->type('string', $comment->author->login),
time => $self->type('dateTime', $comment->{'time'}), time => $self->type('dateTime', $comment->creation_ts),
is_private => $self->type('boolean', $comment->{isprivate}), is_private => $self->type('boolean', $comment->is_private),
text => $self->type('string', $comment->{body}), text => $self->type('string', $comment->body_full),
}; };
} }
......
...@@ -204,7 +204,7 @@ sub process_bug { ...@@ -204,7 +204,7 @@ sub process_bug {
foreach my $field (keys %fields) { foreach my $field (keys %fields) {
$cgi->param(-name => $field, -value => $fields{$field}); $cgi->param(-name => $field, -value => $fields{$field});
} }
$cgi->param('longdesclength', scalar $bug->longdescs); $cgi->param('longdesclength', scalar @{ $bug->comments });
$cgi->param('token', issue_hash_token([$bug->id, $bug->delta_ts])); $cgi->param('token', issue_hash_token([$bug->id, $bug->delta_ts]));
require 'process_bug.cgi'; require 'process_bug.cgi';
......
...@@ -434,17 +434,16 @@ if ($cloned_bug_id) { ...@@ -434,17 +434,16 @@ if ($cloned_bug_id) {
# We need to ensure that we respect the 'insider' status of # We need to ensure that we respect the 'insider' status of
# the first comment, if it has one. Either way, make a note # the first comment, if it has one. Either way, make a note
# that this bug was cloned from another bug. # that this bug was cloned from another bug.
# We cannot use $cloned_bug->longdescs because this method my $bug_desc = $cloned_bug->comments({ order => 'oldest_to_newest' })->[0];
# depends on the "comment_sort_order" user pref, and we my $isprivate = $bug_desc->is_private;
# really want the first comment of the bug.
my $bug_desc = Bugzilla::Bug::GetComments($cloned_bug_id, 'oldest_to_newest');
my $isprivate = $bug_desc->[0]->{'isprivate'};
$vars->{'comment'} = ""; $vars->{'comment'} = "";
$vars->{'commentprivacy'} = 0; $vars->{'commentprivacy'} = 0;
if (!$isprivate || Bugzilla->user->is_insider) { if (!$isprivate || Bugzilla->user->is_insider) {
$vars->{'comment'} = $bug_desc->[0]->{'body'}; # We use "body" to avoid any format_comment text, which would be
# pointless to clone.
$vars->{'comment'} = $bug_desc->body;
$vars->{'commentprivacy'} = $isprivate; $vars->{'commentprivacy'} = $isprivate;
} }
......
...@@ -229,13 +229,13 @@ if (defined($cgi->upload('data')) || $cgi->param('attachurl')) { ...@@ -229,13 +229,13 @@ if (defined($cgi->upload('data')) || $cgi->param('attachurl')) {
# expects to find this exact string. # expects to find this exact string.
my $new_comment = "Created an attachment (id=" . $attachment->id . ")\n" . my $new_comment = "Created an attachment (id=" . $attachment->id . ")\n" .
$attachment->description . "\n"; $attachment->description . "\n";
# We can use $bug->longdescs here because we are sure that the bug # We can use $bug->comments here because we are sure that the bug
# description is of type CMT_NORMAL. No need to include it if it's # description is of type CMT_NORMAL. No need to include it if it's
# empty, though. # empty, though.
if ($bug->longdescs->[0]->{'body'} !~ /^\s+$/) { if ($bug->comments->[0]->body !~ /^\s+$/) {
$new_comment .= "\n" . $bug->longdescs->[0]->{'body'}; $new_comment .= "\n" . $bug->comments->[0]->body;
} }
$bug->update_comment($bug->longdescs->[0]->{'id'}, $new_comment); $bug->update_comment($bug->comments->[0]->id, $new_comment);
} }
else { else {
$vars->{'message'} = 'attachment_creation_failed'; $vars->{'message'} = 'attachment_creation_failed';
......
...@@ -168,8 +168,7 @@ if (defined $cgi->param('delta_ts') ...@@ -168,8 +168,7 @@ if (defined $cgi->param('delta_ts')
$vars->{'start_at'} = $cgi->param('longdesclength'); $vars->{'start_at'} = $cgi->param('longdesclength');
# Always sort midair collision comments oldest to newest, # Always sort midair collision comments oldest to newest,
# regardless of the user's personal preference. # regardless of the user's personal preference.
$vars->{'comments'} = Bugzilla::Bug::GetComments($first_bug->id, $vars->{'comments'} = $first_bug->comments({ order => "oldest_to_newest" });
"oldest_to_newest");
$vars->{'bug'} = $first_bug; $vars->{'bug'} = $first_bug;
# The token contains the old delta_ts. We need a new one. # The token contains the old delta_ts. We need a new one.
$cgi->param('token', issue_hash_token([$first_bug->id, $first_bug->delta_ts])); $cgi->param('token', issue_hash_token([$first_bug->id, $first_bug->delta_ts]));
......
...@@ -94,7 +94,6 @@ ...@@ -94,7 +94,6 @@
[% DEFAULT start_at = 0 mode = "show" %] [% DEFAULT start_at = 0 mode = "show" %]
[% isinsider = Param("insidergroup") && user.in_group(Param("insidergroup")) %]
[% sort_order = user.settings.comment_sort_order.value %] [% sort_order = user.settings.comment_sort_order.value %]
[%# NOTE: (start_at > 0) means we came here from a midair collision, [%# NOTE: (start_at > 0) means we came here from a midair collision,
...@@ -145,8 +144,9 @@ ...@@ -145,8 +144,9 @@
[%############################################################################%] [%############################################################################%]
[% BLOCK a_comment %] [% BLOCK a_comment %]
[% IF NOT comment.isprivate || isinsider %] [% RETURN IF comment.is_private AND ! user.is_insider %]
<div class="bz_comment[% " bz_private" IF comment.isprivate %]
<div class="bz_comment[% " bz_private" IF comment.is_private %]
[% " bz_comment_hilite" IF marks.$count %] [% " bz_comment_hilite" IF marks.$count %]
[% " bz_first_comment" IF count == description %]"> [% " bz_first_comment" IF count == description %]">
[% IF count == description %] [% IF count == description %]
...@@ -168,7 +168,7 @@ ...@@ -168,7 +168,7 @@
</span> </span>
[% END %] [% END %]
[% IF mode == "edit" && isinsider %] [% IF mode == "edit" && user.is_insider %]
<div class="bz_private_checkbox"> <div class="bz_private_checkbox">
<input type="hidden" value="1" <input type="hidden" value="1"
name="defined_isprivate_[% comment.id %]"> name="defined_isprivate_[% comment.id %]">
...@@ -176,7 +176,7 @@ ...@@ -176,7 +176,7 @@
name="isprivate_[% comment.id %]" value="1" name="isprivate_[% comment.id %]" value="1"
id="isprivate_[% comment.id %]" id="isprivate_[% comment.id %]"
onClick="updateCommentPrivacy(this, [% count %])" onClick="updateCommentPrivacy(this, [% count %])"
[% " checked=\"checked\"" IF comment.isprivate %]> [% " checked=\"checked\"" IF comment.is_private %]>
<label for="isprivate_[% comment.id %]">Private</label> <label for="isprivate_[% comment.id %]">Private</label>
</div> </div>
[% END %] [% END %]
...@@ -201,7 +201,7 @@ ...@@ -201,7 +201,7 @@
</span> </span>
<span class="bz_comment_time"> <span class="bz_comment_time">
[%+ comment.time FILTER time %] [%+ comment.creation_ts FILTER time %]
</span> </span>
</div> </div>
...@@ -215,15 +215,9 @@ ...@@ -215,15 +215,9 @@
[%# Don't indent the <pre> block, since then the spaces are displayed in the [%# Don't indent the <pre> block, since then the spaces are displayed in the
# generated HTML # generated HTML
#%] #%]
[% IF comment.already_wrapped %]
[% wrapped_comment = comment.body %]
[% ELSE %]
[% wrapped_comment = comment.body FILTER wrap_comment %]
[% END %]
<pre class="bz_comment_text" <pre class="bz_comment_text"
[% ' id="comment_text_' _ count _ '"' IF mode == "edit" %]> [% ' id="comment_text_' _ count _ '"' IF mode == "edit" %]>
[%- wrapped_comment FILTER quoteUrls(bug, comment) -%] [%- comment.body_full({ wrap => 1 }) FILTER quoteUrls(bug, comment) -%]
</pre> </pre>
</div> </div>
[% END %]
[% END %] [% END %]
...@@ -142,7 +142,7 @@ ...@@ -142,7 +142,7 @@
<form name="changeform" method="post" action="process_bug.cgi"> <form name="changeform" method="post" action="process_bug.cgi">
<input type="hidden" name="delta_ts" value="[% bug.delta_ts %]"> <input type="hidden" name="delta_ts" value="[% bug.delta_ts %]">
<input type="hidden" name="longdesclength" value="[% bug.longdescs.size %]"> <input type="hidden" name="longdesclength" value="[% bug.comments.size %]">
<input type="hidden" name="id" value="[% bug.bug_id %]"> <input type="hidden" name="id" value="[% bug.bug_id %]">
<input type="hidden" name="token" value="[% issue_hash_token([bug.id, bug.delta_ts]) FILTER html %]"> <input type="hidden" name="token" value="[% issue_hash_token([bug.id, bug.delta_ts]) FILTER html %]">
...@@ -287,7 +287,7 @@ ...@@ -287,7 +287,7 @@
<hr> <hr>
<div id="comments"> <div id="comments">
[% PROCESS bug/comments.html.tmpl [% PROCESS bug/comments.html.tmpl
comments = bug.longdescs comments = bug.comments
mode = user.id ? "edit" : "show" mode = user.id ? "edit" : "show"
%] %]
</div> </div>
......
...@@ -23,23 +23,23 @@ ...@@ -23,23 +23,23 @@
#%] #%]
[%# INTERFACE: [%# INTERFACE:
# comment: A hash containing comment information. # comment: A Bugzilla::Comment object.
# count: The comment number (on the bug it belongs to) # is_bugmail: boolean; True if this comment is going into a plain-text
# author: The Bugzilla::User object of the comment's # bugmail.
# author
# time: The time at which the comment has been
# committed
# body: The comment text
# type: One of the CMT_* constants (not given if none
# applies)
# extra_data: Extra data (type specific)
# already_wrapped: Determines whether the comment is pre-wrapped
#%] #%]
[% PROCESS 'global/field-descs.none.tmpl' %] [%# Please don't use field-descs here. It can slow down Bugzilla. %]
[% PROCESS 'global/variables.none.tmpl' %]
[% SET comment_body = comment.body %]
[% IF is_bugmail %]
[% comment_body = comment_body.replace( '(Created an attachment \(id=([0-9]+)\))',
'$1' _ "\n" _ ' --> (' _ urlbase
_ 'attachment.cgi?id=$2)' ) %]
[% END %]
[% IF comment.type == constants.CMT_DUPE_OF %] [% IF comment.type == constants.CMT_DUPE_OF %]
X[% comment.body %] X[% comment_body %]
*** This [% terms.bug %] has been marked as a duplicate of [% terms.bug %] [%+ comment.extra_data %] *** *** This [% terms.bug %] has been marked as a duplicate of [% terms.bug %] [%+ comment.extra_data %] ***
[% ELSIF comment.type == constants.CMT_HAS_DUPE %] [% ELSIF comment.type == constants.CMT_HAS_DUPE %]
...@@ -47,14 +47,13 @@ X[% comment.body %] ...@@ -47,14 +47,13 @@ X[% comment.body %]
[% ELSIF comment.type == constants.CMT_POPULAR_VOTES %] [% ELSIF comment.type == constants.CMT_POPULAR_VOTES %]
*** This [% terms.bug %] has been confirmed by popular vote. *** *** This [% terms.bug %] has been confirmed by popular vote. ***
[% ELSIF comment.type == constants.CMT_MOVED_TO %] [% ELSIF comment.type == constants.CMT_MOVED_TO %]
X[% comment.body %] X[% comment_body %]
[%+ terms.Bug %] moved to [% Param("move-to-url") %]. [%+ terms.Bug %] moved to [% Param("move-to-url") %].
If the move succeeded, [% comment.extra_data %] will receive a mail containing If the move succeeded, [% comment.extra_data %] will receive a mail containing
the number of the new [% terms.bug %] in the other database. the number of the new [% terms.bug %] in the other database.
If all went well, please mark this [% terms.bug %] If all went well, please paste in a link to the new [% terms.bug %].
[%+ display_value("bug_status", 'VERIFIED') %], and paste in a link to the new [% terms.bug %].
Otherwise, reopen this [% terms.bug %]. Otherwise, reopen this [% terms.bug %].
[% ELSE %] [% ELSE %]
X[% comment.body %] X[% comment_body %]
[% END %] [% END %]
...@@ -289,7 +289,7 @@ ...@@ -289,7 +289,7 @@
<br> <br>
[% PROCESS bug/comments.html.tmpl [% PROCESS bug/comments.html.tmpl
comments = bug.longdescs %] comments = bug.comments %]
[% END %] [% END %]
......
...@@ -65,16 +65,16 @@ ...@@ -65,16 +65,16 @@
[% PROCESS section_flags obj => bug %] [% PROCESS section_flags obj => bug %]
[% IF displayfields.long_desc %] [% IF displayfields.long_desc %]
[% FOREACH c = bug.longdescs %] [% FOREACH c = bug.comments %]
[% NEXT IF c.isprivate && !user.in_group(Param("insidergroup")) %] [% NEXT IF c.is_private && !user.in_group(Param("insidergroup")) %]
<long_desc isprivate="[% c.isprivate FILTER xml %]"> <long_desc isprivate="[% c.is_private FILTER xml %]">
<commentid>[% c.id FILTER xml %]</commentid> <commentid>[% c.id FILTER xml %]</commentid>
<who name="[% c.author.name FILTER xml %]">[% c.author.email FILTER email FILTER xml %]</who> <who name="[% c.author.name FILTER xml %]">[% c.author.email FILTER email FILTER xml %]</who>
<bug_when>[% c.time FILTER time("%Y-%m-%d %T %z") FILTER xml %]</bug_when> <bug_when>[% c.creation_ts FILTER time("%Y-%m-%d %T %z") FILTER xml %]</bug_when>
[% IF user.in_group(Param('timetrackinggroup')) && (c.work_time - 0 != 0) %] [% IF user.in_group(Param('timetrackinggroup')) && (c.work_time - 0 != 0) %]
<work_time>[% PROCESS formattimeunit time_unit = c.work_time FILTER xml %]</work_time> <work_time>[% PROCESS formattimeunit time_unit = c.work_time FILTER xml %]</work_time>
[% END %] [% END %]
<thetext>[% c.body FILTER xml %]</thetext> <thetext>[% c.body_full FILTER xml %]</thetext>
</long_desc> </long_desc>
[% END %] [% END %]
[% END %] [% END %]
...@@ -151,4 +151,4 @@ ...@@ -151,4 +151,4 @@
[% END %] [% END %]
/> />
[% END %] [% END %]
[% END %] [% END %]
\ No newline at end of file
...@@ -50,7 +50,7 @@ X-Bugzilla-Changed-Fields: [% changedfields %] ...@@ -50,7 +50,7 @@ X-Bugzilla-Changed-Fields: [% changedfields %]
[%- IF comment.count %] [%- IF comment.count %]
--- Comment #[% comment.count %] from [% comment.author.identity %] [%+ comment.time FILTER time %] --- --- Comment #[% comment.count %] from [% comment.author.identity %] [%+ comment.time FILTER time %] ---
[% END %] [% END %]
[%+ FILTER remove('^X') %][% PROCESS bug/format_comment.txt.tmpl %][% END %] [%+ comment.body_full({ is_bugmail => 1, wrap => 1 }) %]
[% END %] [% END %]
-- [%# Protect the trailing space of the signature marker %] -- [%# Protect the trailing space of the signature marker %]
......
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