From e47e36a6821a4f954f5d03643cdb5be988568798 Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Thu, 28 Nov 2002 18:49:38 +0000 Subject: Bug 171493 - make show_bug use Bug.pm and remove bug_form.pl r=justdave, joel a=justdave --- Bug.pm | 502 ++++++++++++--------- Bugzilla/Bug.pm | 502 ++++++++++++--------- Bugzilla/User.pm | 2 +- bug_form.pl | 398 ---------------- checksetup.pl | 10 +- globals.pl | 19 +- post_bug.cgi | 45 +- process_bug.cgi | 48 +- show_bug.cgi | 38 +- t/004template.t | 2 + template/en/default/bug/comments.html.tmpl | 2 +- template/en/default/bug/create/created.html.tmpl | 20 +- template/en/default/bug/edit.html.tmpl | 101 ++--- template/en/default/bug/process/next.html.tmpl | 13 +- template/en/default/bug/show.html.tmpl | 46 ++ template/en/default/global/code-error.html.tmpl | 4 + .../en/default/global/site-navigation.html.tmpl | 4 +- template/en/default/pages/linked.html.tmpl | 4 +- 18 files changed, 808 insertions(+), 952 deletions(-) delete mode 100644 bug_form.pl create mode 100644 template/en/default/bug/show.html.tmpl diff --git a/Bug.pm b/Bug.pm index 11eb43af1..91c20780a 100755 --- a/Bug.pm +++ b/Bug.pm @@ -21,26 +21,41 @@ # Terry Weissman # Chris Yeh +package Bug; + use strict; -use DBI; use RelationSet; -use vars qw($unconfirmedstate $legal_keywords); +use vars qw($unconfirmedstate $legal_keywords @legal_platform + @legal_priority @legal_severity @legal_opsys @legal_bugs_status + @settable_resolution %components %versions %target_milestone + @enterable_products %milestoneurl %prodmaxvotes); -package Bug; use CGI::Carp qw(fatalsToBrowser); my %ok_field; +use Attachment; use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Flag; +use Bugzilla::FlagType; +use Bugzilla::User; use Bugzilla::Util; for my $key (qw (bug_id alias product version rep_platform op_sys bug_status - resolution priority bug_severity component assigned_to - reporter bug_file_loc short_desc target_milestone - qa_contact status_whiteboard creation_ts - delta_ts votes whoid comment query error) ){ + resolution priority bug_severity component assigned_to + reporter bug_file_loc short_desc target_milestone + qa_contact status_whiteboard creation_ts keywords + delta_ts votes whoid usergroupset comment query error + longdescs cc milestoneurl attachments dependson blocked + cclist_accessible reporter_accessible + isopened isunconfirmed assigned_to_name assigned_to_email + qa_contact_name qa_contact_email reporter_name + reporter_email flag_types num_attachment_flag_types + show_attachment_flags use_keywords any_flags_requesteeble + estimated_time remaining_time actual_time) ) { $ok_field{$key}++; - } +} # create a new empty bug # @@ -95,16 +110,20 @@ sub initBug { $user_id = &::DBname_to_id($user_id); } } - + $self->{'whoid'} = $user_id; my $query = " - select - bugs.bug_id, alias, products.name, version, rep_platform, op_sys, bug_status, - resolution, priority, bug_severity, components.name, assigned_to, reporter, - bug_file_loc, short_desc, target_milestone, qa_contact, - status_whiteboard, date_format(creation_ts,'%Y-%m-%d %H:%i'), - delta_ts, sum(votes.count) + SELECT + bugs.bug_id, alias, bugs.product_id, products.name, version, + rep_platform, op_sys, bug_status, resolution, priority, + bug_severity, bugs.component_id, components.name, assigned_to, + reporter, bug_file_loc, short_desc, target_milestone, + qa_contact, status_whiteboard, + DATE_FORMAT(creation_ts,'%Y.%m.%d %H:%i'), + delta_ts, sum(votes.count), + reporter_accessible, cclist_accessible, + estimated_time, remaining_time from bugs left join votes using(bug_id), products, components where bugs.bug_id = $bug_id @@ -118,14 +137,17 @@ sub initBug { if ((@row = &::FetchSQLData()) && &::CanSeeBug($bug_id, $self->{'whoid'})) { my $count = 0; my %fields; - foreach my $field ("bug_id", "alias", "product", "version", "rep_platform", - "op_sys", "bug_status", "resolution", "priority", - "bug_severity", "component", "assigned_to", "reporter", - "bug_file_loc", "short_desc", "target_milestone", - "qa_contact", "status_whiteboard", "creation_ts", - "delta_ts", "votes") { + foreach my $field ("bug_id", "alias", "product_id", "product", "version", + "rep_platform", "op_sys", "bug_status", "resolution", + "priority", "bug_severity", "component_id", "component", + "assigned_to", "reporter", "bug_file_loc", "short_desc", + "target_milestone", "qa_contact", "status_whiteboard", + "creation_ts", "delta_ts", "votes", + "reporter_accessible", "cclist_accessible". + "estimated_time", "remaining_time") + { $fields{$field} = shift @row; - if ($fields{$field}) { + if (defined $fields{$field}) { $self->{$field} = $fields{$field}; } $count++; @@ -140,8 +162,12 @@ sub initBug { return $self; } - $self->{'assigned_to'} = &::DBID_to_name($self->{'assigned_to'}); - $self->{'reporter'} = &::DBID_to_name($self->{'reporter'}); + $self->{'assigned_to'} = new Bugzilla::User($self->{'assigned_to'}); + $self->{'reporter'} = new Bugzilla::User($self->{'reporter'}); + + if (Param('useqacontact') && $self->{'qa_contact'} > 0) { + $self->{'qa_contact'} = new Bugzilla::User($self->{'qa_contact'}); + } my $ccSet = new RelationSet; $ccSet->mergeFromDB("select who from cc where bug_id=$bug_id"); @@ -150,13 +176,6 @@ sub initBug { $self->{'cc'} = \@cc; } - if (Param("useqacontact") && (defined $self->{'qa_contact'}) ) { - my $name = $self->{'qa_contact'} > 0 ? &::DBID_to_name($self->{'qa_contact'}) :""; - if ($name) { - $self->{'qa_contact'} = $name; - } - } - if (@::legal_keywords) { &::SendSQL("SELECT keyworddefs.name FROM keyworddefs, keywords @@ -172,39 +191,43 @@ sub initBug { } } - &::SendSQL("select attach_id, creation_ts, isprivate, description - from attachments - where bug_id = $bug_id"); - my @attachments; - while (&::MoreSQLData()) { - my ($attachid, $date, $isprivate, $desc) = (&::FetchSQLData()); - my %attach; - $attach{'attachid'} = $attachid; - $attach{'isprivate'} = $isprivate; - $attach{'date'} = $date; - $attach{'desc'} = $desc; - push @attachments, \%attach; - } - if (@attachments) { - $self->{'attachments'} = \@attachments; - } - - &::SendSQL("select bug_id, who, bug_when, isprivate, thetext - from longdescs - where bug_id = $bug_id"); - my @longdescs; - while (&::MoreSQLData()) { - my ($bug_id, $who, $bug_when, $isprivate, $thetext) = (&::FetchSQLData()); - my %longdesc; - $longdesc{'who'} = $who; - $longdesc{'bug_when'} = $bug_when; - $longdesc{'isprivate'} = $isprivate; - $longdesc{'thetext'} = $thetext; - push @longdescs, \%longdesc; - } - if (@longdescs) { - $self->{'longdescs'} = \@longdescs; - } + $self->{'attachments'} = Attachment::query($self->{bug_id}); + + # The types of flags that can be set on this bug. + # If none, no UI for setting flags will be displayed. + my $flag_types = + Bugzilla::FlagType::match({ 'target_type' => 'bug', + 'product_id' => $self->{'product_id'}, + 'component_id' => $self->{'component_id'} }); + foreach my $flag_type (@$flag_types) { + $flag_type->{'flags'} = + Bugzilla::Flag::match({ 'bug_id' => $self->{bug_id}, + 'type_id' => $flag_type->{'id'}, + 'target_type' => 'bug' }); + } + $self->{'flag_types'} = $flag_types; + $self->{'any_flags_requesteeable'} = grep($_->{'is_requesteeble'}, @$flag_types); + + # The number of types of flags that can be set on attachments to this bug + # and the number of flags on those attachments. One of these counts must be + # greater than zero in order for the "flags" column to appear in the table + # of attachments. + my $num_attachment_flag_types = + Bugzilla::FlagType::count({ 'target_type' => 'attachment', + 'product_id' => $self->{'product_id'}, + 'component_id' => $self->{'component_id'}, + 'is_active' => 1 }); + my $num_attachment_flags = + Bugzilla::Flag::count({ 'target_type' => 'attachment', + 'bug_id' => $self->{bug_id} }); + + $self->{'show_attachment_flags'} + = $num_attachment_flag_types || $num_attachment_flags; + + $self->{'milestoneurl'} = $::milestoneurl{$self->{product}}; + + $self->{'isunconfirmed'} = ($self->{bug_status} eq $::unconfirmedstate); + $self->{'isopened'} = &::IsOpenedState($self->{bug_status}); my @depends = EmitDependList("blocked", "dependson", $bug_id); if (@depends) { @@ -218,7 +241,191 @@ sub initBug { return $self; } +sub actual_time { + my ($self) = @_; + + return $self->{'actual_time'} if exists $self->{'actual_time'}; + + if (&::UserInGroup(Param("timetrackinggroup"))) { + &::SendSQL("SELECT SUM(work_time) + FROM longdescs WHERE longdescs.bug_id=$self->{bug_id}"); + $self->{'actual_time'} = &::FetchSQLData(); + } + + return $self->{'actual_time'}; +} + +sub longdescs { + my ($self) = @_; + + return $self->{'longdescs'} if exists $self->{'longdescs'}; + + $self->{'longdescs'} = &::GetComments($self->{bug_id}); + + return $self->{'longdescs'}; +} + +sub use_keywords { + return @::legal_keywords; +} + +sub use_votes { + my ($self) = @_; + + return Param('usevotes') + && $::prodmaxvotes{$self->{product}} > 0; +} + +sub groups { + my $self = shift; + + return $self->{'groups'} if exists $self->{'groups'}; + + my @groups; + + # Some of this stuff needs to go into Bugzilla::User + + # For every group, we need to know if there is ANY bug_group_map + # record putting the current bug in that group and if there is ANY + # user_group_map record putting the user in that group. + # The LEFT JOINs are checking for record existence. + # + &::SendSQL("SELECT DISTINCT groups.id, name, description," . + " bug_group_map.group_id IS NOT NULL," . + " user_group_map.group_id IS NOT NULL," . + " isactive, membercontrol, othercontrol" . + " FROM groups" . + " LEFT JOIN bug_group_map" . + " ON bug_group_map.group_id = groups.id" . + " AND bug_id = $self->{'bug_id'}" . + " LEFT JOIN user_group_map" . + " ON user_group_map.group_id = groups.id" . + " AND user_id = $::userid" . + " AND NOT isbless" . + " LEFT JOIN group_control_map" . + " ON group_control_map.group_id = groups.id" . + " AND group_control_map.product_id = " . $self->{'product_id'} . + " WHERE isbuggroup"); + + while (&::MoreSQLData()) { + my ($groupid, $name, $description, $ison, $ingroup, $isactive, + $membercontrol, $othercontrol) = &::FetchSQLData(); + + $membercontrol ||= 0; + + # For product groups, we only want to use the group if either + # (1) The bit is set and not required, or + # (2) The group is Shown or Default for members and + # the user is a member of the group. + if ($ison || + ($isactive && $ingroup + && (($membercontrol == CONTROLMAPDEFAULT) + || ($membercontrol == CONTROLMAPSHOWN)) + )) + { + my $ismandatory = $isactive + && ($membercontrol == CONTROLMAPMANDATORY); + + push (@groups, { "bit" => $groupid, + "ison" => $ison, + "ingroup" => $ingroup, + "mandatory" => $ismandatory, + "description" => $description }); + } + } + + $self->{'groups'} = \@groups; + + return $self->{'groups'}; +} + +sub user { + my $self = shift; + return $self->{'user'} if exists $self->{'user'}; + + $self->{'user'} = {}; + + my $movers = Param("movers"); + $self->{'user'}->{'canmove'} = Param("move-enabled") + && (defined $::COOKIE{"Bugzilla_login"}) + && ($::COOKIE{"Bugzilla_login"} =~ /$movers/); + + # In the below, if the person hasn't logged in ($::userid == 0), then + # we treat them as if they can do anything. That's because we don't + # know why they haven't logged in; it may just be because they don't + # use cookies. Display everything as if they have all the permissions + # in the world; their permissions will get checked when they log in + # and actually try to make the change. + $self->{'user'}->{'canedit'} = $::userid == 0 + || $::userid == $self->{'reporter'} + || $::userid == $self->{'qa_contact'} + || $::userid == $self->{'assigned_to'} + || &::UserInGroup("editbugs"); + $self->{'user'}->{'canconfirm'} = ($::userid == 0) + || &::UserInGroup("canconfirm") + || &::UserInGroup("editbugs"); + + return $self->{'user'}; +} + +sub choices { + my $self = shift; + return $self->{'choices'} if exists $self->{'choices'}; + + &::GetVersionTable(); + + $self->{'choices'} = {}; + # Fiddle the product list. + my $seen_curr_prod; + my @prodlist; + + foreach my $product (@::enterable_products) { + if ($product eq $self->{'product'}) { + # if it's the product the bug is already in, it's ALWAYS in + # the popup, period, whether the user can see it or not, and + # regardless of the disallownew setting. + $seen_curr_prod = 1; + push(@prodlist, $product); + next; + } + + if (!&::CanEnterProduct($product)) { + # If we're using bug groups to restrict entry on products, and + # this product has an entry group, and the user is not in that + # group, we don't want to include that product in this list. + next; + } + + push(@prodlist, $product); + } + + # The current product is part of the popup, even if new bugs are no longer + # allowed for that product + if (!$seen_curr_prod) { + push (@prodlist, $self->{'product'}); + @prodlist = sort @prodlist; + } + + # Hack - this array contains "". See bug 106589. + my @res = grep ($_, @::settable_resolution); + + $self->{'choices'} = + { + 'product' => \@prodlist, + 'rep_platform' => \@::legal_platform, + 'priority' => \@::legal_priority, + 'bug_severity' => \@::legal_severity, + 'op_sys' => \@::legal_opsys, + 'bug_status' => \@::legal_bugs_status, + 'resolution' => \@res, + 'component' => $::components{$self->{product}}, + 'version' => $::versions{$self->{product}}, + 'target_milestone' => $::target_milestone{$self->{product}}, + }; + + return $self->{'choices'}; +} # given a bug hash, emit xml for it. with file header provided by caller # @@ -261,11 +468,11 @@ sub emitXML { && Param("insidergroup") && !&::UserInGroup(Param("insidergroup"))); $xml .= " \n"; - $xml .= " " . &::DBID_to_name($self->{'longdescs'}[$i]->{'who'}) + $xml .= " " . $self->{'longdescs'}[$i]->{'email'} . "\n"; - $xml .= " " . $self->{'longdescs'}[$i]->{'bug_when'} + $xml .= " " . $self->{'longdescs'}[$i]->{'time'} . "\n"; - $xml .= " " . QuoteXMLChars($self->{'longdescs'}[$i]->{'thetext'}) + $xml .= " " . QuoteXMLChars($self->{'longdescs'}[$i]->{'body'}) . "\n"; $xml .= " \n"; } @@ -280,7 +487,7 @@ sub emitXML { $xml .= " " . $self->{'attachments'}[$i]->{'attachid'} . "\n"; $xml .= " " . $self->{'attachments'}[$i]->{'date'} . "\n"; - $xml .= " " . QuoteXMLChars($self->{'attachments'}[$i]->{'desc'}) . "\n"; + $xml .= " " . QuoteXMLChars($self->{'attachments'}[$i]->{'description'}) . "\n"; # $xml .= " " . $self->{'attachments'}[$i]->{'type'} . "\n"; # $xml .= " " . $self->{'attachments'}[$i]->{'data'} . "\n"; $xml .= " \n"; @@ -310,8 +517,8 @@ sub QuoteXMLChars { $_[0] =~ s/&/&/g; $_[0] =~ s//>/g; - $_[0] =~ s/'/'/g; - $_[0] =~ s/"/"/g; + $_[0] =~ s/\'/'/g; + $_[0] =~ s/\"/"/g; # $_[0] =~ s/([\x80-\xFF])/&XmlUtf8Encode(ord($1))/ge; return($_[0]); } @@ -341,156 +548,25 @@ sub XML_Footer { return ("\n"); } -sub CanChangeField { - my $self = shift(); - my ($f, $oldvalue, $newvalue) = (@_); - my $UserInEditGroupSet = -1; - my $UserInCanConfirmGroupSet = -1; - my $ownerid; - my $reporterid; - my $qacontactid; - - if ($f eq "assigned_to" || $f eq "reporter" || $f eq "qa_contact") { - if ($oldvalue =~ /^\d+$/) { - if ($oldvalue == 0) { - $oldvalue = ""; - } else { - $oldvalue = &::DBID_to_name($oldvalue); - } - } - } - if ($oldvalue eq $newvalue) { - return 1; - } - if (trim($oldvalue) eq trim($newvalue)) { - return 1; - } - if ($f =~ /^longdesc/) { - return 1; - } - if ($UserInEditGroupSet < 0) { - $UserInEditGroupSet = UserInGroup($self, "editbugs"); - } - if ($UserInEditGroupSet) { - return 1; - } - &::SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs " . - "WHERE bug_id = $self->{'bug_id'}"); - ($reporterid, $ownerid, $qacontactid) = (&::FetchSQLData()); - - # Let reporter change bug status, even if they can't edit bugs. - # If reporter can't re-open their bug they will just file a duplicate. - # While we're at it, let them close their own bugs as well. - if ( ($f eq "bug_status") && ($self->{'whoid'} eq $reporterid) ) { - return 1; - } - if ($f eq "bug_status" && $newvalue ne $::unconfirmedstate && - &::IsOpenedState($newvalue)) { - - # Hmm. They are trying to set this bug to some opened state - # that isn't the UNCONFIRMED state. Are they in the right - # group? Or, has it ever been confirmed? If not, then this - # isn't legal. - - if ($UserInCanConfirmGroupSet < 0) { - $UserInCanConfirmGroupSet = &::UserInGroup("canconfirm"); - } - if ($UserInCanConfirmGroupSet) { - return 1; - } - &::SendSQL("SELECT everconfirmed FROM bugs WHERE bug_id = $self->{'bug_id'}"); - my $everconfirmed = FetchOneColumn(); - if ($everconfirmed) { - return 1; - } - } elsif ($reporterid eq $self->{'whoid'} || $ownerid eq $self->{'whoid'} || - $qacontactid eq $self->{'whoid'}) { - return 1; - } - $self->{'error'} = " -Only the owner or submitter of the bug, or a sufficiently -empowered user, may make that change to the $f field." -} - -sub Collision { - my $self = shift(); - my $write = "WRITE"; # Might want to make a param to control - # whether we do LOW_PRIORITY ... - &::SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " . - "cc AS selectVisible_cc $write, " . - "profiles $write, dependencies $write, votes $write, " . - "keywords $write, longdescs $write, fielddefs $write, " . - "keyworddefs READ, groups READ, attachments READ, products READ"); - &::SendSQL("SELECT delta_ts FROM bugs where bug_id=$self->{'bug_id'}"); - my $delta_ts = &::FetchOneColumn(); - &::SendSQL("unlock tables"); - if ($self->{'delta_ts'} ne $delta_ts) { - return 1; - } - else { - return 0; - } -} - -sub AppendComment { - my $self = shift(); - my ($comment) = (@_); - $comment =~ s/\r\n/\n/g; # Get rid of windows-style line endings. - $comment =~ s/\r/\n/g; # Get rid of mac-style line endings. - if ($comment =~ /^\s*$/) { # Nothin' but whitespace. - return; - } - - &::SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext) " . - "VALUES($self->{'bug_id'}, $self->{'whoid'}, now(), " . &::SqlQuote($comment) . ")"); - - &::SendSQL("UPDATE bugs SET delta_ts = now() WHERE bug_id = $self->{'bug_id'}"); -} - - -#from o'reilley's Programming Perl -sub display { - my $self = shift; - my @keys; - if (@_ == 0) { # no further arguments - @keys = sort keys(%$self); - } else { - @keys = @_; # use the ones given - } - foreach my $key (@keys) { - print "\t$key => $self->{$key}\n"; - } -} - -sub CommitChanges { - -#snapshot bug -#snapshot dependencies -#check can change fields -#check collision -#lock and change fields -#notify through mail - -} - sub AUTOLOAD { use vars qw($AUTOLOAD); - my $self = shift; - my $type = ref($self) || $self; my $attr = $AUTOLOAD; $attr =~ s/.*:://; return unless $attr=~ /[^A-Z]/; - if (@_) { - $self->{$attr} = shift; - return; - } confess ("invalid bug attribute $attr") unless $ok_field{$attr}; - if (defined $self->{$attr}) { - return $self->{$attr}; - } else { - return ''; - } + + no strict 'refs'; + *$AUTOLOAD = sub { + my $self = shift; + if (defined $self->{$attr}) { + return $self->{$attr}; + } else { + return ''; + } + }; + + goto &$AUTOLOAD; } 1; diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 11eb43af1..91c20780a 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -21,26 +21,41 @@ # Terry Weissman # Chris Yeh +package Bug; + use strict; -use DBI; use RelationSet; -use vars qw($unconfirmedstate $legal_keywords); +use vars qw($unconfirmedstate $legal_keywords @legal_platform + @legal_priority @legal_severity @legal_opsys @legal_bugs_status + @settable_resolution %components %versions %target_milestone + @enterable_products %milestoneurl %prodmaxvotes); -package Bug; use CGI::Carp qw(fatalsToBrowser); my %ok_field; +use Attachment; use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Flag; +use Bugzilla::FlagType; +use Bugzilla::User; use Bugzilla::Util; for my $key (qw (bug_id alias product version rep_platform op_sys bug_status - resolution priority bug_severity component assigned_to - reporter bug_file_loc short_desc target_milestone - qa_contact status_whiteboard creation_ts - delta_ts votes whoid comment query error) ){ + resolution priority bug_severity component assigned_to + reporter bug_file_loc short_desc target_milestone + qa_contact status_whiteboard creation_ts keywords + delta_ts votes whoid usergroupset comment query error + longdescs cc milestoneurl attachments dependson blocked + cclist_accessible reporter_accessible + isopened isunconfirmed assigned_to_name assigned_to_email + qa_contact_name qa_contact_email reporter_name + reporter_email flag_types num_attachment_flag_types + show_attachment_flags use_keywords any_flags_requesteeble + estimated_time remaining_time actual_time) ) { $ok_field{$key}++; - } +} # create a new empty bug # @@ -95,16 +110,20 @@ sub initBug { $user_id = &::DBname_to_id($user_id); } } - + $self->{'whoid'} = $user_id; my $query = " - select - bugs.bug_id, alias, products.name, version, rep_platform, op_sys, bug_status, - resolution, priority, bug_severity, components.name, assigned_to, reporter, - bug_file_loc, short_desc, target_milestone, qa_contact, - status_whiteboard, date_format(creation_ts,'%Y-%m-%d %H:%i'), - delta_ts, sum(votes.count) + SELECT + bugs.bug_id, alias, bugs.product_id, products.name, version, + rep_platform, op_sys, bug_status, resolution, priority, + bug_severity, bugs.component_id, components.name, assigned_to, + reporter, bug_file_loc, short_desc, target_milestone, + qa_contact, status_whiteboard, + DATE_FORMAT(creation_ts,'%Y.%m.%d %H:%i'), + delta_ts, sum(votes.count), + reporter_accessible, cclist_accessible, + estimated_time, remaining_time from bugs left join votes using(bug_id), products, components where bugs.bug_id = $bug_id @@ -118,14 +137,17 @@ sub initBug { if ((@row = &::FetchSQLData()) && &::CanSeeBug($bug_id, $self->{'whoid'})) { my $count = 0; my %fields; - foreach my $field ("bug_id", "alias", "product", "version", "rep_platform", - "op_sys", "bug_status", "resolution", "priority", - "bug_severity", "component", "assigned_to", "reporter", - "bug_file_loc", "short_desc", "target_milestone", - "qa_contact", "status_whiteboard", "creation_ts", - "delta_ts", "votes") { + foreach my $field ("bug_id", "alias", "product_id", "product", "version", + "rep_platform", "op_sys", "bug_status", "resolution", + "priority", "bug_severity", "component_id", "component", + "assigned_to", "reporter", "bug_file_loc", "short_desc", + "target_milestone", "qa_contact", "status_whiteboard", + "creation_ts", "delta_ts", "votes", + "reporter_accessible", "cclist_accessible". + "estimated_time", "remaining_time") + { $fields{$field} = shift @row; - if ($fields{$field}) { + if (defined $fields{$field}) { $self->{$field} = $fields{$field}; } $count++; @@ -140,8 +162,12 @@ sub initBug { return $self; } - $self->{'assigned_to'} = &::DBID_to_name($self->{'assigned_to'}); - $self->{'reporter'} = &::DBID_to_name($self->{'reporter'}); + $self->{'assigned_to'} = new Bugzilla::User($self->{'assigned_to'}); + $self->{'reporter'} = new Bugzilla::User($self->{'reporter'}); + + if (Param('useqacontact') && $self->{'qa_contact'} > 0) { + $self->{'qa_contact'} = new Bugzilla::User($self->{'qa_contact'}); + } my $ccSet = new RelationSet; $ccSet->mergeFromDB("select who from cc where bug_id=$bug_id"); @@ -150,13 +176,6 @@ sub initBug { $self->{'cc'} = \@cc; } - if (Param("useqacontact") && (defined $self->{'qa_contact'}) ) { - my $name = $self->{'qa_contact'} > 0 ? &::DBID_to_name($self->{'qa_contact'}) :""; - if ($name) { - $self->{'qa_contact'} = $name; - } - } - if (@::legal_keywords) { &::SendSQL("SELECT keyworddefs.name FROM keyworddefs, keywords @@ -172,39 +191,43 @@ sub initBug { } } - &::SendSQL("select attach_id, creation_ts, isprivate, description - from attachments - where bug_id = $bug_id"); - my @attachments; - while (&::MoreSQLData()) { - my ($attachid, $date, $isprivate, $desc) = (&::FetchSQLData()); - my %attach; - $attach{'attachid'} = $attachid; - $attach{'isprivate'} = $isprivate; - $attach{'date'} = $date; - $attach{'desc'} = $desc; - push @attachments, \%attach; - } - if (@attachments) { - $self->{'attachments'} = \@attachments; - } - - &::SendSQL("select bug_id, who, bug_when, isprivate, thetext - from longdescs - where bug_id = $bug_id"); - my @longdescs; - while (&::MoreSQLData()) { - my ($bug_id, $who, $bug_when, $isprivate, $thetext) = (&::FetchSQLData()); - my %longdesc; - $longdesc{'who'} = $who; - $longdesc{'bug_when'} = $bug_when; - $longdesc{'isprivate'} = $isprivate; - $longdesc{'thetext'} = $thetext; - push @longdescs, \%longdesc; - } - if (@longdescs) { - $self->{'longdescs'} = \@longdescs; - } + $self->{'attachments'} = Attachment::query($self->{bug_id}); + + # The types of flags that can be set on this bug. + # If none, no UI for setting flags will be displayed. + my $flag_types = + Bugzilla::FlagType::match({ 'target_type' => 'bug', + 'product_id' => $self->{'product_id'}, + 'component_id' => $self->{'component_id'} }); + foreach my $flag_type (@$flag_types) { + $flag_type->{'flags'} = + Bugzilla::Flag::match({ 'bug_id' => $self->{bug_id}, + 'type_id' => $flag_type->{'id'}, + 'target_type' => 'bug' }); + } + $self->{'flag_types'} = $flag_types; + $self->{'any_flags_requesteeable'} = grep($_->{'is_requesteeble'}, @$flag_types); + + # The number of types of flags that can be set on attachments to this bug + # and the number of flags on those attachments. One of these counts must be + # greater than zero in order for the "flags" column to appear in the table + # of attachments. + my $num_attachment_flag_types = + Bugzilla::FlagType::count({ 'target_type' => 'attachment', + 'product_id' => $self->{'product_id'}, + 'component_id' => $self->{'component_id'}, + 'is_active' => 1 }); + my $num_attachment_flags = + Bugzilla::Flag::count({ 'target_type' => 'attachment', + 'bug_id' => $self->{bug_id} }); + + $self->{'show_attachment_flags'} + = $num_attachment_flag_types || $num_attachment_flags; + + $self->{'milestoneurl'} = $::milestoneurl{$self->{product}}; + + $self->{'isunconfirmed'} = ($self->{bug_status} eq $::unconfirmedstate); + $self->{'isopened'} = &::IsOpenedState($self->{bug_status}); my @depends = EmitDependList("blocked", "dependson", $bug_id); if (@depends) { @@ -218,7 +241,191 @@ sub initBug { return $self; } +sub actual_time { + my ($self) = @_; + + return $self->{'actual_time'} if exists $self->{'actual_time'}; + + if (&::UserInGroup(Param("timetrackinggroup"))) { + &::SendSQL("SELECT SUM(work_time) + FROM longdescs WHERE longdescs.bug_id=$self->{bug_id}"); + $self->{'actual_time'} = &::FetchSQLData(); + } + + return $self->{'actual_time'}; +} + +sub longdescs { + my ($self) = @_; + + return $self->{'longdescs'} if exists $self->{'longdescs'}; + + $self->{'longdescs'} = &::GetComments($self->{bug_id}); + + return $self->{'longdescs'}; +} + +sub use_keywords { + return @::legal_keywords; +} + +sub use_votes { + my ($self) = @_; + + return Param('usevotes') + && $::prodmaxvotes{$self->{product}} > 0; +} + +sub groups { + my $self = shift; + + return $self->{'groups'} if exists $self->{'groups'}; + + my @groups; + + # Some of this stuff needs to go into Bugzilla::User + + # For every group, we need to know if there is ANY bug_group_map + # record putting the current bug in that group and if there is ANY + # user_group_map record putting the user in that group. + # The LEFT JOINs are checking for record existence. + # + &::SendSQL("SELECT DISTINCT groups.id, name, description," . + " bug_group_map.group_id IS NOT NULL," . + " user_group_map.group_id IS NOT NULL," . + " isactive, membercontrol, othercontrol" . + " FROM groups" . + " LEFT JOIN bug_group_map" . + " ON bug_group_map.group_id = groups.id" . + " AND bug_id = $self->{'bug_id'}" . + " LEFT JOIN user_group_map" . + " ON user_group_map.group_id = groups.id" . + " AND user_id = $::userid" . + " AND NOT isbless" . + " LEFT JOIN group_control_map" . + " ON group_control_map.group_id = groups.id" . + " AND group_control_map.product_id = " . $self->{'product_id'} . + " WHERE isbuggroup"); + + while (&::MoreSQLData()) { + my ($groupid, $name, $description, $ison, $ingroup, $isactive, + $membercontrol, $othercontrol) = &::FetchSQLData(); + + $membercontrol ||= 0; + + # For product groups, we only want to use the group if either + # (1) The bit is set and not required, or + # (2) The group is Shown or Default for members and + # the user is a member of the group. + if ($ison || + ($isactive && $ingroup + && (($membercontrol == CONTROLMAPDEFAULT) + || ($membercontrol == CONTROLMAPSHOWN)) + )) + { + my $ismandatory = $isactive + && ($membercontrol == CONTROLMAPMANDATORY); + + push (@groups, { "bit" => $groupid, + "ison" => $ison, + "ingroup" => $ingroup, + "mandatory" => $ismandatory, + "description" => $description }); + } + } + + $self->{'groups'} = \@groups; + + return $self->{'groups'}; +} + +sub user { + my $self = shift; + return $self->{'user'} if exists $self->{'user'}; + + $self->{'user'} = {}; + + my $movers = Param("movers"); + $self->{'user'}->{'canmove'} = Param("move-enabled") + && (defined $::COOKIE{"Bugzilla_login"}) + && ($::COOKIE{"Bugzilla_login"} =~ /$movers/); + + # In the below, if the person hasn't logged in ($::userid == 0), then + # we treat them as if they can do anything. That's because we don't + # know why they haven't logged in; it may just be because they don't + # use cookies. Display everything as if they have all the permissions + # in the world; their permissions will get checked when they log in + # and actually try to make the change. + $self->{'user'}->{'canedit'} = $::userid == 0 + || $::userid == $self->{'reporter'} + || $::userid == $self->{'qa_contact'} + || $::userid == $self->{'assigned_to'} + || &::UserInGroup("editbugs"); + $self->{'user'}->{'canconfirm'} = ($::userid == 0) + || &::UserInGroup("canconfirm") + || &::UserInGroup("editbugs"); + + return $self->{'user'}; +} + +sub choices { + my $self = shift; + return $self->{'choices'} if exists $self->{'choices'}; + + &::GetVersionTable(); + + $self->{'choices'} = {}; + # Fiddle the product list. + my $seen_curr_prod; + my @prodlist; + + foreach my $product (@::enterable_products) { + if ($product eq $self->{'product'}) { + # if it's the product the bug is already in, it's ALWAYS in + # the popup, period, whether the user can see it or not, and + # regardless of the disallownew setting. + $seen_curr_prod = 1; + push(@prodlist, $product); + next; + } + + if (!&::CanEnterProduct($product)) { + # If we're using bug groups to restrict entry on products, and + # this product has an entry group, and the user is not in that + # group, we don't want to include that product in this list. + next; + } + + push(@prodlist, $product); + } + + # The current product is part of the popup, even if new bugs are no longer + # allowed for that product + if (!$seen_curr_prod) { + push (@prodlist, $self->{'product'}); + @prodlist = sort @prodlist; + } + + # Hack - this array contains "". See bug 106589. + my @res = grep ($_, @::settable_resolution); + + $self->{'choices'} = + { + 'product' => \@prodlist, + 'rep_platform' => \@::legal_platform, + 'priority' => \@::legal_priority, + 'bug_severity' => \@::legal_severity, + 'op_sys' => \@::legal_opsys, + 'bug_status' => \@::legal_bugs_status, + 'resolution' => \@res, + 'component' => $::components{$self->{product}}, + 'version' => $::versions{$self->{product}}, + 'target_milestone' => $::target_milestone{$self->{product}}, + }; + + return $self->{'choices'}; +} # given a bug hash, emit xml for it. with file header provided by caller # @@ -261,11 +468,11 @@ sub emitXML { && Param("insidergroup") && !&::UserInGroup(Param("insidergroup"))); $xml .= " \n"; - $xml .= " " . &::DBID_to_name($self->{'longdescs'}[$i]->{'who'}) + $xml .= " " . $self->{'longdescs'}[$i]->{'email'} . "\n"; - $xml .= " " . $self->{'longdescs'}[$i]->{'bug_when'} + $xml .= " " . $self->{'longdescs'}[$i]->{'time'} . "\n"; - $xml .= " " . QuoteXMLChars($self->{'longdescs'}[$i]->{'thetext'}) + $xml .= " " . QuoteXMLChars($self->{'longdescs'}[$i]->{'body'}) . "\n"; $xml .= " \n"; } @@ -280,7 +487,7 @@ sub emitXML { $xml .= " " . $self->{'attachments'}[$i]->{'attachid'} . "\n"; $xml .= " " . $self->{'attachments'}[$i]->{'date'} . "\n"; - $xml .= " " . QuoteXMLChars($self->{'attachments'}[$i]->{'desc'}) . "\n"; + $xml .= " " . QuoteXMLChars($self->{'attachments'}[$i]->{'description'}) . "\n"; # $xml .= " " . $self->{'attachments'}[$i]->{'type'} . "\n"; # $xml .= " " . $self->{'attachments'}[$i]->{'data'} . "\n"; $xml .= " \n"; @@ -310,8 +517,8 @@ sub QuoteXMLChars { $_[0] =~ s/&/&/g; $_[0] =~ s//>/g; - $_[0] =~ s/'/'/g; - $_[0] =~ s/"/"/g; + $_[0] =~ s/\'/'/g; + $_[0] =~ s/\"/"/g; # $_[0] =~ s/([\x80-\xFF])/&XmlUtf8Encode(ord($1))/ge; return($_[0]); } @@ -341,156 +548,25 @@ sub XML_Footer { return ("\n"); } -sub CanChangeField { - my $self = shift(); - my ($f, $oldvalue, $newvalue) = (@_); - my $UserInEditGroupSet = -1; - my $UserInCanConfirmGroupSet = -1; - my $ownerid; - my $reporterid; - my $qacontactid; - - if ($f eq "assigned_to" || $f eq "reporter" || $f eq "qa_contact") { - if ($oldvalue =~ /^\d+$/) { - if ($oldvalue == 0) { - $oldvalue = ""; - } else { - $oldvalue = &::DBID_to_name($oldvalue); - } - } - } - if ($oldvalue eq $newvalue) { - return 1; - } - if (trim($oldvalue) eq trim($newvalue)) { - return 1; - } - if ($f =~ /^longdesc/) { - return 1; - } - if ($UserInEditGroupSet < 0) { - $UserInEditGroupSet = UserInGroup($self, "editbugs"); - } - if ($UserInEditGroupSet) { - return 1; - } - &::SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs " . - "WHERE bug_id = $self->{'bug_id'}"); - ($reporterid, $ownerid, $qacontactid) = (&::FetchSQLData()); - - # Let reporter change bug status, even if they can't edit bugs. - # If reporter can't re-open their bug they will just file a duplicate. - # While we're at it, let them close their own bugs as well. - if ( ($f eq "bug_status") && ($self->{'whoid'} eq $reporterid) ) { - return 1; - } - if ($f eq "bug_status" && $newvalue ne $::unconfirmedstate && - &::IsOpenedState($newvalue)) { - - # Hmm. They are trying to set this bug to some opened state - # that isn't the UNCONFIRMED state. Are they in the right - # group? Or, has it ever been confirmed? If not, then this - # isn't legal. - - if ($UserInCanConfirmGroupSet < 0) { - $UserInCanConfirmGroupSet = &::UserInGroup("canconfirm"); - } - if ($UserInCanConfirmGroupSet) { - return 1; - } - &::SendSQL("SELECT everconfirmed FROM bugs WHERE bug_id = $self->{'bug_id'}"); - my $everconfirmed = FetchOneColumn(); - if ($everconfirmed) { - return 1; - } - } elsif ($reporterid eq $self->{'whoid'} || $ownerid eq $self->{'whoid'} || - $qacontactid eq $self->{'whoid'}) { - return 1; - } - $self->{'error'} = " -Only the owner or submitter of the bug, or a sufficiently -empowered user, may make that change to the $f field." -} - -sub Collision { - my $self = shift(); - my $write = "WRITE"; # Might want to make a param to control - # whether we do LOW_PRIORITY ... - &::SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " . - "cc AS selectVisible_cc $write, " . - "profiles $write, dependencies $write, votes $write, " . - "keywords $write, longdescs $write, fielddefs $write, " . - "keyworddefs READ, groups READ, attachments READ, products READ"); - &::SendSQL("SELECT delta_ts FROM bugs where bug_id=$self->{'bug_id'}"); - my $delta_ts = &::FetchOneColumn(); - &::SendSQL("unlock tables"); - if ($self->{'delta_ts'} ne $delta_ts) { - return 1; - } - else { - return 0; - } -} - -sub AppendComment { - my $self = shift(); - my ($comment) = (@_); - $comment =~ s/\r\n/\n/g; # Get rid of windows-style line endings. - $comment =~ s/\r/\n/g; # Get rid of mac-style line endings. - if ($comment =~ /^\s*$/) { # Nothin' but whitespace. - return; - } - - &::SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext) " . - "VALUES($self->{'bug_id'}, $self->{'whoid'}, now(), " . &::SqlQuote($comment) . ")"); - - &::SendSQL("UPDATE bugs SET delta_ts = now() WHERE bug_id = $self->{'bug_id'}"); -} - - -#from o'reilley's Programming Perl -sub display { - my $self = shift; - my @keys; - if (@_ == 0) { # no further arguments - @keys = sort keys(%$self); - } else { - @keys = @_; # use the ones given - } - foreach my $key (@keys) { - print "\t$key => $self->{$key}\n"; - } -} - -sub CommitChanges { - -#snapshot bug -#snapshot dependencies -#check can change fields -#check collision -#lock and change fields -#notify through mail - -} - sub AUTOLOAD { use vars qw($AUTOLOAD); - my $self = shift; - my $type = ref($self) || $self; my $attr = $AUTOLOAD; $attr =~ s/.*:://; return unless $attr=~ /[^A-Z]/; - if (@_) { - $self->{$attr} = shift; - return; - } confess ("invalid bug attribute $attr") unless $ok_field{$attr}; - if (defined $self->{$attr}) { - return $self->{$attr}; - } else { - return ''; - } + + no strict 'refs'; + *$AUTOLOAD = sub { + my $self = shift; + if (defined $self->{$attr}) { + return $self->{$attr}; + } else { + return ''; + } + }; + + goto &$AUTOLOAD; } 1; diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index a45cf0976..46f520b77 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -59,7 +59,7 @@ sub new { } $self->{'name'} = $name; - $self->{'email'} = $email; + $self->{'email'} = $email || "__UNKNOWN__"; $self->{'exists'} = $exists; # Generate a string to identify the user by name + email if the user diff --git a/bug_form.pl b/bug_form.pl deleted file mode 100644 index b4a2ef678..000000000 --- a/bug_form.pl +++ /dev/null @@ -1,398 +0,0 @@ -# -*- 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 Netscape Communications -# Corporation. Portions created by Netscape are -# Copyright (C) 1998 Netscape Communications Corporation. All -# Rights Reserved. -# -# Contributor(s): Terry Weissman -# Dave Miller -# Vaskin Kissoyan - -use strict; - -use RelationSet; -use Bugzilla::Constants; -# Use the Attachment module to display attachments for the bug. -use Attachment; - -# Use the Flag modules to display flags on the bug. -use Bugzilla::Flag; -use Bugzilla::FlagType; - -sub show_bug { - # Shut up misguided -w warnings about "used only once". For some reason, - # "use vars" chokes on me when I try it here. - sub bug_form_pl_sillyness { - my $zz; - $zz = %::FORM; - $zz = %::proddesc; - $zz = %::prodmaxvotes; - $zz = @::enterable_products; - $zz = @::settable_resolution; - $zz = $::unconfirmedstate; - $zz = $::milestoneurl; - $zz = $::template; - $zz = $::vars; - $zz = @::legal_priority; - $zz = @::legal_platform; - $zz = @::legal_severity; - $zz = @::legal_bug_status; - $zz = @::target_milestone; - $zz = @::components; - $zz = @::legal_keywords; - $zz = @::versions; - $zz = @::legal_opsys; - } - - # Use templates - my $template = $::template; - my $vars = $::vars; - - $vars->{'GetBugLink'} = \&GetBugLink; - $vars->{'lsearch'} = \&lsearch, - $vars->{'header_done'} = (@_), - - quietly_check_login(); - - my $id = $::FORM{'id'}; - - if (!defined($id)) { - $template->process("bug/choose.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - exit; - } - - my %user = %{$vars->{'user'}}; - my %bug; - - # Populate the bug hash with the info we get directly from the DB. - my $query = " - SELECT bugs.bug_id, alias, bugs.product_id, products.name, version, - rep_platform, op_sys, bug_status, resolution, priority, - bug_severity, bugs.component_id, components.name, assigned_to, - reporter, bug_file_loc, short_desc, target_milestone, - qa_contact, status_whiteboard, - DATE_FORMAT(creation_ts,'%Y.%m.%d %H:%i'), delta_ts, sum(votes.count), - estimated_time, remaining_time - FROM bugs LEFT JOIN votes USING(bug_id), products, components - WHERE bugs.bug_id = $id - AND bugs.product_id = products.id - AND bugs.component_id = components.id - GROUP BY bugs.bug_id"; - - SendSQL($query); - - # The caller is meant to have checked this. Abort here so that - # we don't get obscure SQL errors, below - if (!MoreSQLData()) { - $vars->{'bug_id'} = $id; - ThrowCodeError("no_bug_data"); - } - - my $value; - my $disp_date; - my @row = FetchSQLData(); - foreach my $field ("bug_id", "alias", "product_id", "product", "version", - "rep_platform", "op_sys", "bug_status", "resolution", - "priority", "bug_severity", "component_id", "component", - "assigned_to", "reporter", "bug_file_loc", "short_desc", - "target_milestone", "qa_contact", "status_whiteboard", - "creation_ts", "delta_ts", "votes", - "estimated_time", "remaining_time") - { - $value = shift(@row); - $bug{$field} = defined($value) ? $value : ""; - } - - # General arrays of info about the database state - GetVersionTable(); - - # Fiddle the product list. - my $seen_curr_prod; - my @prodlist; - - foreach my $product (@::enterable_products) { - if ($product eq $bug{'product'}) { - # if it's the product the bug is already in, it's ALWAYS in - # the popup, period, whether the user can see it or not, and - # regardless of the disallownew setting. - $seen_curr_prod = 1; - push(@prodlist, $product); - next; - } - - if (!CanEnterProduct($product)) { - # If we're using bug groups to restrict entry on products, and - # this product has an entry group, and the user is not in that - # group, we don't want to include that product in this list. - next; - } - - push(@prodlist, $product); - } - - # The current product is part of the popup, even if new bugs are no longer - # allowed for that product - if (!$seen_curr_prod) { - push (@prodlist, $bug{'product'}); - @prodlist = sort @prodlist; - } - - $vars->{'product'} = \@prodlist; - $vars->{'rep_platform'} = \@::legal_platform; - $vars->{'priority'} = \@::legal_priority; - $vars->{'bug_severity'} = \@::legal_severity; - $vars->{'op_sys'} = \@::legal_opsys; - $vars->{'bug_status'} = \@::legal_bug_status; - - # Hack - this array contains "" for some reason. See bug 106589. - shift @::settable_resolution; - $vars->{'resolution'} = \@::settable_resolution; - - $vars->{'component_'} = $::components{$bug{'product'}}; - $vars->{'version'} = $::versions{$bug{'product'}}; - $vars->{'target_milestone'} = $::target_milestone{$bug{'product'}}; - $bug{'milestoneurl'} = $::milestoneurl{$bug{'product'}} || - "notargetmilestone.html"; - - $vars->{'use_votes'} = Param('usevotes') - && $::prodmaxvotes{$bug{'product'}} > 0; - - # Add additional, calculated fields to the bug hash - if (@::legal_keywords) { - $vars->{'use_keywords'} = 1; - - SendSQL("SELECT keyworddefs.name - FROM keyworddefs, keywords - WHERE keywords.bug_id = $id - AND keyworddefs.id = keywords.keywordid - ORDER BY keyworddefs.name"); - my @keywords; - while (MoreSQLData()) { - push(@keywords, FetchOneColumn()); - } - - $bug{'keywords'} = \@keywords; - } - - # Attachments - $bug{'attachments'} = Attachment::query($id); - - # The types of flags that can be set on this bug. - # If none, no UI for setting flags will be displayed. - my $flag_types = - Bugzilla::FlagType::match({ 'target_type' => 'bug', - 'product_id' => $bug{'product_id'}, - 'component_id' => $bug{'component_id'} }); - foreach my $flag_type (@$flag_types) { - $flag_type->{'flags'} = - Bugzilla::Flag::match({ 'bug_id' => $id , - 'type_id' => $flag_type->{'id'} , - 'target_type' => 'bug' }); - } - $vars->{'flag_types'} = $flag_types; - $vars->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types); - - # The number of types of flags that can be set on attachments to this bug - # and the number of flags on those attachments. One of these counts must be - # greater than zero in order for the "flags" column to appear in the table - # of attachments. - my $num_attachment_flag_types = - Bugzilla::FlagType::count({ 'target_type' => 'attachment', - 'product_id' => $bug{'product_id'}, - 'component_id' => $bug{'component_id'}, - 'is_active' => 1 }); - my $num_attachment_flags = - Bugzilla::Flag::count({ 'target_type' => 'attachment', - 'bug_id' => $id }); - - $vars->{'show_attachment_flags'} - = $num_attachment_flag_types || $num_attachment_flags; - - # Dependencies - my @list; - SendSQL("SELECT dependson FROM dependencies WHERE - blocked = $id ORDER BY dependson"); - while (MoreSQLData()) { - my ($i) = FetchSQLData(); - push(@list, $i); - } - - if (UserInGroup(Param("timetrackinggroup"))) { - - SendSQL("SELECT SUM(work_time) - FROM longdescs WHERE longdescs.bug_id=$id"); - $bug{'actual_time'} = FetchSQLData(); - - } - - $bug{'dependson'} = \@list; - - my @list2; - SendSQL("SELECT blocked FROM dependencies WHERE - dependson = $id ORDER BY blocked"); - while (MoreSQLData()) { - my ($i) = FetchSQLData(); - push(@list2, $i); - } - - $bug{'blocked'} = \@list2; - - # Groups - my @groups; - - # For every group, we need to know if there is ANY bug_group_map - # record putting the current bug in that group and if there is ANY - # user_group_map record putting the user in that group. - # The LEFT JOINs are checking for record existence. - # - SendSQL("SELECT DISTINCT groups.id, name, description," . - " bug_group_map.group_id IS NOT NULL," . - " user_group_map.group_id IS NOT NULL," . - " isactive, membercontrol, othercontrol" . - " FROM groups" . - " LEFT JOIN bug_group_map" . - " ON bug_group_map.group_id = groups.id" . - " AND bug_id = $bug{'bug_id'}" . - " LEFT JOIN user_group_map" . - " ON user_group_map.group_id = groups.id" . - " AND user_id = $::userid" . - " AND NOT isbless" . - " LEFT JOIN group_control_map" . - " ON group_control_map.group_id = groups.id" . - " AND group_control_map.product_id = " . $bug{'product_id'} . - " WHERE isbuggroup"); - - $user{'inallgroups'} = 1; - - while (MoreSQLData()) { - my ($groupid, $name, $description, $ison, $ingroup, $isactive, - $membercontrol, $othercontrol) = FetchSQLData(); - - $bug{'inagroup'} = 1 if ($ison); - $membercontrol ||= 0; - - if ($isactive && ($membercontrol == CONTROLMAPMANDATORY)) { - $bug{'inagroup'} = 1; - } - # For product groups, we only want to display the checkbox if either - # (1) The bit is set and not required, or - # (2) The group is Shown or Default for members and - # the user is a member of the group. - if ($ison || - ($isactive && $ingroup - && (($membercontrol == CONTROLMAPDEFAULT) - || ($membercontrol == CONTROLMAPSHOWN)) - )) - { - $user{'inallgroups'} &= $ingroup; - - my $mandatory; - if ($isactive && ($membercontrol == CONTROLMAPMANDATORY)) { - $mandatory = 1; - } else { - $mandatory = 0; - } - if (($ison) || ($ingroup)) { - push (@groups, { "bit" => $groupid, - "ison" => $ison, - "ingroup" => $ingroup, - "mandatory" => $mandatory, - "description" => $description }); - } - } - } - - # If the bug is restricted to a group, get flags that allow - # the user to set whether or not the reporter - # and cc list can see the bug even if they are not members of all - # groups to which the bug is restricted. - if ($bug{'inagroup'}) { - - # Determine whether or not the bug is always accessible by the - # reporter, QA contact, and/or users on the cc: list. - SendSQL("SELECT reporter_accessible, cclist_accessible - FROM bugs - WHERE bug_id = $id - "); - ($bug{'reporter_accessible'}, - $bug{'cclist_accessible'}) = FetchSQLData(); - } - $vars->{'groups'} = \@groups; - - my $movers = Param("movers"); - $user{'canmove'} = Param("move-enabled") - && (defined $::COOKIE{"Bugzilla_login"}) - && ($::COOKIE{"Bugzilla_login"} =~ /$movers/); - - # User permissions - - # In the below, if the person hasn't logged in ($::userid == 0), then - # we treat them as if they can do anything. That's because we don't - # know why they haven't logged in; it may just be because they don't - # use cookies. Display everything as if they have all the permissions - # in the world; their permissions will get checked when they log in - # and actually try to make the change. - $user{'canedit'} = $::userid == 0 - || $::userid == $bug{'reporter'} - || $::userid == $bug{'qa_contact'} - || $::userid == $bug{'assigned_to'} - || UserInGroup("editbugs"); - $user{'canconfirm'} = ($::userid == 0) - || UserInGroup("canconfirm") - || UserInGroup("editbugs"); - - # Bug states - $bug{'isunconfirmed'} = ($bug{'bug_status'} eq $::unconfirmedstate); - $bug{'isopened'} = IsOpenedState($bug{'bug_status'}); - - # People involved with the bug - $bug{'assigned_to_email'} = DBID_to_name($bug{'assigned_to'}); - $bug{'assigned_to'} = DBID_to_real_or_loginname($bug{'assigned_to'}); - $bug{'reporter'} = DBID_to_real_or_loginname($bug{'reporter'}); - $bug{'qa_contact'} = $bug{'qa_contact'} > 0 ? - DBID_to_name($bug{'qa_contact'}) : ""; - - my $ccset = new RelationSet; - $ccset->mergeFromDB("SELECT who FROM cc WHERE bug_id=$id"); - - my @cc = $ccset->toArrayOfStrings(); - $bug{'cc'} = \@cc if $cc[0]; - - # Next bug in list (if there is one) - my @bug_list; - if ($::COOKIE{"BUGLIST"} && $id) - { - @bug_list = split(/:/, $::COOKIE{"BUGLIST"}); - } - $vars->{'bug_list'} = \@bug_list; - - $bug{'comments'} = GetComments($bug{'bug_id'}); - - # This is length in number of comments - $bug{'longdesclength'} = scalar(@{$bug{'comments'}}); - - # Add the bug and user hashes to the variables - $vars->{'bug'} = \%bug; - $vars->{'user'} = \%user; - - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("bug/edit.html.tmpl", $vars) - || ThrowTemplateError($template->error()); -} - -1; - diff --git a/checksetup.pl b/checksetup.pl index 101583096..aceb6a706 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -921,14 +921,6 @@ END } { - eval("use Date::Parse"); - # Templates will be recompiled if the source changes, but not if the - # settings in globals.pl change, so we need to be able to force a rebuild - # if that happens - - # The last time the global template params were changed. Keep in UTC, - # YYYY-MM-DD - my $lastTemplateParamChange = str2time("2002-04-27", "UTC"); if (-e 'data/template') { print "Removing existing compiled templates ...\n" unless $silent; @@ -967,6 +959,8 @@ END js => sub { return $_; }, html_linebreak => sub { return $_; }, url_quote => sub { return $_; }, + quoteUrls => sub { return $_; }, + bug_link => [ sub { return sub { return $_; } }, 1], csv => sub { return $_; }, time => sub { return $_; }, }, diff --git a/globals.pl b/globals.pl index 547fd1b95..79c2f92b4 100644 --- a/globals.pl +++ b/globals.pl @@ -1781,8 +1781,20 @@ $::template ||= Template->new( # characters NOT in the regex set: [a-zA-Z0-9_\-.]. The 'uri' # filter should be used for a full URL that may have # characters that need encoding. - url_quote => \&Bugzilla::Util::url_quote, - + url_quote => \&Bugzilla::Util::url_quote , + + quoteUrls => \"eUrls , + + bug_link => [ sub { + my ($context, $bug) = @_; + return sub { + my $text = shift; + return GetBugLink($text, $bug); + }; + }, + 1 + ], + # In CSV, quotes are doubled, and we enclose the whole value in quotes csv => sub { @@ -1892,9 +1904,6 @@ $::vars = # Generic linear search function 'lsearch' => \&Bugzilla::Util::lsearch , - # quoteUrls - autolinkifies text - 'quoteUrls' => \"eUrls , - # UserInGroup - you probably want to cache this 'UserInGroup' => \&UserInGroup , diff --git a/post_bug.cgi b/post_bug.cgi index 3ddfbe689..2a65c2436 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -28,7 +28,8 @@ use lib qw(.); use Bugzilla::Constants; require "CGI.pl"; -require "bug_form.pl"; + +use Bug; use Bugzilla::User; @@ -491,28 +492,38 @@ close(PMAIL); # Tell the user all about it $vars->{'id'} = $id; -$vars->{'mail'} = $mailresults; -$vars->{'type'} = "created"; +my $bug = new Bug($id, $::userid); +$vars->{'bug'} = $bug; -print "Content-type: text/html\n\n"; -$template->process("bug/create/created.html.tmpl", $vars) - || ThrowTemplateError($template->error()); +ThrowCodeError("bug_error") if $bug->error; + +$vars->{'sentmail'} = []; + +push (@{$vars->{'sentmail'}}, { type => 'created', + id => $id, + mail => $mailresults + }); foreach my $i (@all_deps) { - $vars->{'mail'} = ""; - open(PMAIL, "-|") or exec('./processmail', $i, $::COOKIE{'Bugzilla_login'}); $vars->{'mail'} .= $_ while ; + my $mail = ""; + open(PMAIL, "-|") or exec('./processmail', $i, $::COOKIE{'Bugzilla_login'}); + $mail .= $_ while ; close(PMAIL); - $vars->{'id'} = $i; - $vars->{'type'} = "dep"; + push (@{$vars->{'sentmail'}}, { type => 'dep', + id => $i, + mail => $mail + }); +} - # Let the user know we checked to see if we should email notice - # of this new bug to users with a relationship to the depenedant - # bug and who did and didn't receive email about it - $template->process("bug/process/results.html.tmpl", $vars) - || ThrowTemplateError($template->error()); +my @bug_list; +if ($::COOKIE{"BUGLIST"}) { + @bug_list = split(/:/, $::COOKIE{"BUGLIST"}); } +$vars->{'bug_list'} = \@bug_list; + +print "Content-type: text/html\n\n"; +$template->process("bug/create/created.html.tmpl", $vars) + || ThrowTemplateError($template->error()); -$::FORM{'id'} = $id; -show_bug("header is already done"); diff --git a/process_bug.cgi b/process_bug.cgi index a59e439dc..80b318a74 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -33,8 +33,8 @@ use lib qw(.); use Bugzilla::Constants; require "CGI.pl"; -require "bug_form.pl"; +use Bug; use Bugzilla::User; use RelationSet; @@ -53,7 +53,7 @@ use vars qw(%versions %settable_resolution %target_milestone %legal_severity - $next_bug); + ); ConnectToDatabase(); my $whoid = confirm_login(); @@ -151,6 +151,23 @@ if (defined($::FORM{'id'})) { } } +# Set up the vars for nagiavtional elements +my $next_bug; +if ($::COOKIE{"BUGLIST"} && $::FORM{'id'}) { + my @bug_list = split(/:/, $::COOKIE{"BUGLIST"}); + $vars->{'bug_list'} = \@bug_list; + my $cur = lsearch(\@bug_list, $::FORM{"id"}); + if ($cur >= 0 && $cur < $#bug_list) { + $next_bug = $bug_list[$cur + 1]; + + # Note that we only bother with the bug_id here, and get + # the full bug object at the end, before showing the edit + # page. If you change this, remember that we have not + # done the security checks on the next bug yet + $vars->{'bug'} = { bug_id => $next_bug }; + } +} + # Start displaying the response page. $template->process("bug/process/header.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -1725,26 +1742,17 @@ foreach my $id (@idlist) { } } -# Show next bug, if it exists. -if ($::COOKIE{"BUGLIST"} && $::FORM{'id'}) { - my @bugs = split(/:/, $::COOKIE{"BUGLIST"}); - $vars->{'bug_list'} = \@bugs; - my $cur = lsearch(\@bugs, $::FORM{"id"}); - if ($cur >= 0 && $cur < $#bugs) { - my $next_bug = $bugs[$cur + 1]; - if (detaint_natural($next_bug) && CanSeeBug($next_bug, $::userid)) { - $::FORM{'id'} = $next_bug; - - $vars->{'next_id'} = $next_bug; - - # Let the user know we are about to display the next bug in their list. - $template->process("bug/process/next.html.tmpl", $vars) - || ThrowTemplateError($template->error()); +# now show the next bug +if ($next_bug) { + if (detaint_natural($next_bug) && CanSeeBug($next_bug, $::userid)) { + my $bug = new Bug($next_bug, $::userid); + $vars->{'bug'} = $bug; + ThrowCodeError("bug_error") if $bug->error; - show_bug("header is already done"); + $template->process("bug/process/next.html.tmpl", $vars) + || ThrowTemplateError($template->error()); - exit; - } + exit; } } diff --git a/show_bug.cgi b/show_bug.cgi index 6711ff7e6..377c7905d 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -25,10 +25,13 @@ use strict; use lib qw(.); require "CGI.pl"; -require "bug_form.pl"; ConnectToDatabase(); +use vars qw($template $vars $userid); + +use Bug; + if ($::FORM{'GoAheadAndLogIn'}) { confirm_login(); } else { @@ -39,11 +42,20 @@ if ($::FORM{'GoAheadAndLogIn'}) { # Begin Data/Security Validation ###################################################################### +unless (defined ($::FORM{'id'})) { + my $format = GetFormat("bug/choose", $::FORM{'format'}, $::FORM{'ctype'}); + + print "Content-type: $format->{'contenttype'}\n\n"; + $template->process("$format->{'template'}", $vars) || + ThrowTemplateError($template->error()); + exit; +} + +my $format = GetFormat("bug/show", $::FORM{'format'}, $::FORM{'ctype'}); + # Make sure the bug ID is a positive integer representing an existing # bug that the user is authorized to access. -if (defined ($::FORM{'id'})) { - ValidateBugID($::FORM{'id'}); -} +ValidateBugID($::FORM{'id'}); ###################################################################### # End Data/Security Validation @@ -51,6 +63,20 @@ if (defined ($::FORM{'id'})) { GetVersionTable(); -print "Content-type: text/html\n\n"; +my $bug = new Bug($::FORM{'id'}, $userid); + +$vars->{'bug'} = $bug; + +ThrowCodeError("bug_error") if $bug->error; + +# Next bug in list (if there is one) +my @bug_list; +if ($::COOKIE{"BUGLIST"}) { + @bug_list = split(/:/, $::COOKIE{"BUGLIST"}); +} +$vars->{'bug_list'} = \@bug_list; + +print "Content-type: $format->{'ctype'}\n\n"; +$template->process("$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); -show_bug(); diff --git a/t/004template.t b/t/004template.t index 6c44fca48..b3fdcc8b7 100644 --- a/t/004template.t +++ b/t/004template.t @@ -81,6 +81,8 @@ my $provider = Template::Provider->new( js => sub { return $_ } , strike => sub { return $_ } , url_quote => sub { return $_ } , + quoteUrls => sub { return $_ } , + bug_link => [ sub { return sub { return $_; } }, 1] , csv => sub { return $_ } , time => sub { return $_ } , }, diff --git a/template/en/default/bug/comments.html.tmpl b/template/en/default/bug/comments.html.tmpl index 42971b327..98d7ae386 100644 --- a/template/en/default/bug/comments.html.tmpl +++ b/template/en/default/bug/comments.html.tmpl @@ -68,7 +68,7 @@ # generated HTML #%]
-  [%- quoteUrls(comment.body) -%]
+  [%- comment.body FILTER quoteUrls -%]
 
[% END %] diff --git a/template/en/default/bug/create/created.html.tmpl b/template/en/default/bug/create/created.html.tmpl index 0264413a7..5966e4e0e 100644 --- a/template/en/default/bug/create/created.html.tmpl +++ b/template/en/default/bug/create/created.html.tmpl @@ -23,8 +23,24 @@ title = "Bug $id Submitted" %] -[% PROCESS bug/process/results.html.tmpl %] +[% FOREACH item = sentmail %] + [% PROCESS bug/process/results.html.tmpl + type = item.type + id = item.id + mail = item.mail + %] +[% END %]
-[%# post_bug.cgi will add a copy of the filed bug below here %] +
+ +[% PROCESS bug/edit.html.tmpl %] + +
+ +[% PROCESS bug/navigate.html.tmpl %] + +
+ +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index aa56678dd..56483a779 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -20,19 +20,6 @@ # Vaskin Kissoyan #%] -[% filtered_desc = bug.short_desc FILTER html %] -[% filtered_timestamp = bug.delta_ts FILTER time %] -[% UNLESS header_done %] - [% PROCESS global/header.html.tmpl - title = "Bug $bug.bug_id - $bug.short_desc" - h1 = "Bugzilla Bug $bug.bug_id" - h2 = filtered_desc - h3 = "Last modified: $filtered_timestamp" - style_urls = [ "css/edit_bug.css" ] - %] -[% END %] - -[% PROCESS bug/navigate.html.tmpl %] [% PROCESS bug/time.html.tmpl %] [% IF UserInGroup(Param('timetrackinggroup')) %] @@ -59,12 +46,10 @@ [% END %] -
-
- + [%# *** Hardware Reporter Product OS AddCC *** %] @@ -96,7 +81,7 @@ Reporter: - [% bug.reporter FILTER html %] + [% bug.reporter.identity FILTER html %] @@ -128,19 +113,7 @@ Component: - - - - -   + [% PROCESS select selname => "component" accesskey => "m" %] Version: @@ -203,14 +176,17 @@ Assigned To: - [% bug.assigned_to FILTER html %] + [% bug.assigned_to.identity FILTER html %]   [% IF Param("usetargetmilestone") && bug.target_milestone %] - Target - Milestone: + [% IF bug.milestoneurl %] + + [% END %] + Target Milestone: + [% "" IF bug.milestoneurl %] [% PROCESS select selname = "target_milestone" accesskey => "t" %] @@ -228,7 +204,7 @@ + value="[% bug.qa_contact.email FILTER html %]" size="60"> [% END %] @@ -248,8 +224,10 @@ value="[% bug.bug_file_loc FILTER html %]" size="60"> - [% IF flag_types.size > 0 %] - [% PROCESS "flag/list.html.tmpl" %] + [% IF bug.flag_types.size > 0 %] + [% PROCESS "flag/list.html.tmpl" + flag_types = bug.flag_types + any_flags_requesteeble = bug.any_flags_requesteeble %] [% END %] @@ -276,7 +254,7 @@ [% END %] - [% IF use_keywords %] + [% IF bug.use_keywords %] @@ -350,7 +328,10 @@ [% PROCESS attachment/list.html.tmpl attachments = bug.attachments - bugid = bug.bug_id %] + bugid = bug.bug_id + num_attachment_flag_types = bug.num_attachment_flag_types + show_attachment_flags = bug.show_attachment_flags + %] [%# *** Dependencies Votes *** %] @@ -405,9 +386,13 @@ accesskey="c">
- [% IF groups.size > 0 %] + [% IF bug.groups.size > 0 %] + [% inallgroups = 1 %] + [% inagroup = 0 %] + [% FOREACH group = bug.groups %] + [% SET inallgroups = 0 IF NOT group.ingroup %] + [% SET inagroup = 1 IF group.ison %] - [% FOREACH group = groups %] [% IF NOT group.mandatory %] [% IF NOT emitted_description %] [% emitted_description = 1 %] @@ -430,7 +415,7 @@ [% END %] [% END %] - [% IF NOT user.inallgroups %] + [% IF NOT inallgroups %] Only members of a group can change the visibility of a bug for that group @@ -438,7 +423,7 @@
[% END %] - [% IF bug.inagroup %] + [% IF inagroup %]

Users in the roles selected below can always view this bug:
@@ -472,16 +457,16 @@ [% knum = 1 %] [% IF bug.bug_status == "UNCONFIRMED" && - user.canconfirm %] + bug.user.canconfirm %] Confirm bug (change status to NEW)
[% knum = knum + 1 %] [% END %] - [% IF user.canedit %] + [% IF bug.user.canedit %] [% IF bug.isopened %] - [% IF bug.bug_status != "ASSIGNED" && user.canconfirm %] + [% IF bug.bug_status != "ASSIGNED" && bug.user.canconfirm %] Accept bug ( [% "confirm bug, " IF bug.isunconfirmed %]change @@ -501,7 +486,7 @@ Resolve bug, changing resolution to @@ -523,9 +508,9 @@ (this.value != '')) { document.changeform.knob[[% knum %]].checked=true; }" - value="[% bug.assigned_to_email FILTER html %]"> + value="[% bug.assigned_to.email FILTER html %]">
- [% IF bug.isunconfirmed && user.canconfirm %] + [% IF bug.isunconfirmed && bug.user.canconfirm %]      and confirm bug (change status to NEW)
@@ -537,7 +522,7 @@ [% " and QA contact" IF Param('useqacontact') %] of selected component
- [% IF bug.isunconfirmed && user.canconfirm %] + [% IF bug.isunconfirmed && bug.user.canconfirm %]      and confirm bug (change status to NEW)
@@ -545,7 +530,7 @@ [% knum = knum + 1 %] [% ELSE %] [% IF bug.resolution != "MOVED" || - (bug.resolution == "MOVED" && user.canmove) %] + (bug.resolution == "MOVED" && bug.user.canmove) %] Reopen bug
[% knum = knum + 1 %] @@ -574,7 +559,7 @@
- [% IF user.canmove %] + [% IF bug.user.canmove %]   |   @@ -598,19 +583,11 @@


[% PROCESS bug/comments.html.tmpl - comments = bug.comments + comments = bug.longdescs mode = "edit" %] -
- -[% PROCESS bug/navigate.html.tmpl %] - -
- -[% PROCESS global/footer.html.tmpl %] - [%############################################################################%] [%# Block for dependencies #%] @@ -620,7 +597,7 @@ Bug [% bug.bug_id %] [%+ dep.title %]: [% FOREACH depbug = bug.${dep.fieldname} %] - [% GetBugLink(depbug, depbug) %][% " " %] + [% depbug FILTER bug_link(depbug) %][% " " %] [% END %] @@ -638,7 +615,7 @@