From 5d1bca95620daf504093e781e26bea10357c9850 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Fri, 31 Aug 2001 11:19:32 +0000 Subject: Fix for bug 84338: initial implementation of attachment tracker, which lets users flag attachments with statuses. Patch by Myk Melez r=justdave@syndicomm.com --- attachment.cgi | 519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 519 insertions(+) create mode 100755 attachment.cgi (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi new file mode 100755 index 000000000..e0b723734 --- /dev/null +++ b/attachment.cgi @@ -0,0 +1,519 @@ +#!/usr/bonsaitools/bin/perl -w +# -*- 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 +# Myk Melez + +################################################################################ +# Script Initialization +################################################################################ + +# Make it harder for us to do dangerous things in Perl. +use diagnostics; +use strict; + +# Include the Bugzilla CGI and general utility library. +require "CGI.pl"; + +# Establish a connection to the database backend. +ConnectToDatabase(); + +# Use the template toolkit (http://www.template-toolkit.org/) to generate +# the user interface (HTML pages and mail messages) using templates in the +# "template/" subdirectory. +use Template; + +# Create the global template object that processes templates and specify +# configuration parameters that apply to all templates processed in this script. +my $template = Template->new( + { + # Colon-separated list of directories containing templates. + INCLUDE_PATH => "template/default" , + # Allow templates to be specified with relative paths. + RELATIVE => 1 + } +); + +# Define the global variables and functions that will be passed to the UI +# template. Individual functions add their own values to this hash before +# sending them to the templates they process. +my $vars = + { + # Function for retrieving global parameters. + 'Param' => \&Param , + + # Function for processing global parameters that contain references + # to other global parameters. + 'PerformSubsts' => \&PerformSubsts + }; + +# Check whether or not the user is logged in and, if so, set the $::userid +# and $::usergroupset variables. +quietly_check_login(); + +################################################################################ +# Main Body Execution +################################################################################ + +# All calls to this script should contain an "action" variable whose value +# determines what the user wants to do. The code below checks the value of +# that variable and runs the appropriate code. + +# Determine whether to use the action specified by the user or the default. +my $action = $::FORM{'action'} || 'view'; + +if ($action eq "view") +{ + validateID(); + view(); +} +elsif ($action eq "viewall") +{ + ValidateBugID($::FORM{'bugid'}); + viewall(); +} +elsif ($action eq "edit") +{ + validateID(); + edit(); +} +elsif ($action eq "update") +{ + confirm_login(); + UserInGroup("editbugs") + || DisplayError("You are not authorized to edit attachments.") + && exit; + validateID(); + validateDescription(); + validateMIMEType(); + validateIsPatch(); + validateIsObsolete(); + validateStatuses(); + update(); +} +else +{ + DisplayError("I could not figure out what you wanted to do.") +} + +exit; + +################################################################################ +# Data Validation / Security Authorization +################################################################################ + +sub validateID +{ + # Validate the value of the "id" form field, which must contain a positive + # integer that is the ID of an existing attachment. + + $::FORM{'id'} =~ /^[1-9][0-9]*$/ + || DisplayError("You did not enter a valid attachment number.") + && exit; + + # Make sure the attachment exists in the database. + SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); + MoreSQLData() + || DisplayError("Attachment #$::FORM{'id'} does not exist.") + && exit; + + # Make sure the user is authorized to access this attachment's bug. + my ($bugid) = FetchSQLData(); + ValidateBugID($bugid); +} + +sub validateDescription +{ + $::FORM{'description'} + || DisplayError("You must enter a description for the attachment.") + && exit; +} + +sub validateMIMEType +{ + $::FORM{'mimetype'} =~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ + || DisplayError("You must enter a valid MIME type of the form foo/bar + where foo is either application, audio, image, message, + model, multipart, text, or video.") + && exit; +} + +sub validateIsPatch +{ + # Set the ispatch flag to zero if it is undefined, since the UI uses + # an HTML checkbox to represent this flag, and unchecked HTML checkboxes + # do not get sent in HTML requests. + $::FORM{'ispatch'} = $::FORM{'ispatch'} ? 1 : 0; +} + +sub validateIsObsolete +{ + # Set the isobsolete flag to zero if it is undefined, since the UI uses + # an HTML checkbox to represent this flag, and unchecked HTML checkboxes + # do not get sent in HTML requests. + $::FORM{'isobsolete'} = $::FORM{'isobsolete'} ? 1 : 0; +} + +sub validateStatuses +{ + # Get a list of attachment statuses that are valid for this attachment. + PushGlobalSQLState(); + SendSQL("SELECT attachstatusdefs.id + FROM attachments, bugs, attachstatusdefs + WHERE attachments.attach_id = $::FORM{'id'} + AND attachments.bug_id = bugs.bug_id + AND attachstatusdefs.product = bugs.product"); + my @statusdefs; + push(@statusdefs, FetchSQLData()) while MoreSQLData(); + PopGlobalSQLState(); + + foreach my $status (@{$::MFORM{'status'}}) + { + grep($_ == $status, @statusdefs) + || DisplayError("One of the statuses you entered is not a valid status + for this attachment.") + && exit; + } +} + +################################################################################ +# Functions +################################################################################ + +sub view +{ + # Display an attachment. + + # Retrieve the attachment content and its MIME type from the database. + SendSQL("SELECT mimetype, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); + my ($mimetype, $thedata) = FetchSQLData(); + + # Return the appropriate HTTP response headers. + print "Content-Type: $mimetype\n\n"; + + print $thedata; +} + + +sub viewall +{ + # Display all attachments for a given bug in a series of IFRAMEs within one HTML page. + + # Retrieve the attachments from the database and write them into an array + # of hashes where each hash represents one attachment. + SendSQL("SELECT attach_id, creation_ts, mimetype, description, ispatch, isobsolete + FROM attachments WHERE bug_id = $::FORM{'bugid'} ORDER BY attach_id"); + my @attachments; # the attachments array + while ( MoreSQLData() ) { + my %a; # the attachment hash + ($a{'attachid'}, $a{'date'}, $a{'mimetype'}, + $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = FetchSQLData(); + + # Format the attachment's creation/modification date into something readable. + if ($a{'date'} =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { + $a{'date'} = "$3/$4/$2 $5:$6"; + } + + # Quote HTML characters (&<>) in the description and MIME Type. + $a{'description'} = value_quote($a{'description'}); + $a{'mimetype'} = value_quote($a{'mimetype'}); + + # Flag attachments as to whether or not they can be viewed (as opposed to + # being downloaded). Currently I decide they are viewable if their MIME type + # is either text/*, image/*, or application/vnd.mozilla.*. + # !!! Yuck, what an ugly hack. Fix it! + $a{'isviewable'} = ( $a{'mimetype'} =~ /^(text|image|application\/vnd\.mozilla\.)/ ); + + # Retrieve a list of status flags that have been set on the attachment. + PushGlobalSQLState(); + SendSQL("SELECT name + FROM attachstatuses, attachstatusdefs + WHERE attach_id = $a{'attachid'} + AND attachstatuses.statusid = attachstatusdefs.id + ORDER BY sortkey"); + my @statuses; + push(@statuses, FetchSQLData()) while MoreSQLData(); + $a{'statuses'} = \@statuses; + PopGlobalSQLState(); + + # Add the hash representing the attachment to the array of attachments. + push @attachments, \%a; + } + + # Retrieve the bug summary for displaying on screen. + SendSQL("SELECT short_desc FROM bugs WHERE bug_id = $::FORM{'bugid'}"); + my ($bugsummary) = FetchSQLData(); + + # Define the variables and functions that will be passed to the UI template. + $vars->{'bugid'} = $::FORM{'bugid'}; + $vars->{'bugsummary'} = $bugsummary; + $vars->{'attachments'} = \@attachments; + + # Return the appropriate HTTP response headers. + print "Content-Type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/viewall.atml", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; +} + + +sub edit +{ + # Edit an attachment record. Users with "editbugs" privileges can edit the + # attachment's description, MIME type, ispatch and isobsolete flags, and + # statuses, and they can also submit a comment that appears in the bug. + # Users cannot edit the content of the attachment itself. + + # Retrieve the attachment from the database. + SendSQL("SELECT description, mimetype, bug_id, ispatch, isobsolete + FROM attachments WHERE attach_id = $::FORM{'id'}"); + my ($description, $mimetype, $bugid, $ispatch, $isobsolete) = FetchSQLData(); + + # Flag attachment as to whether or not it can be viewed (as opposed to + # being downloaded). Currently I decide it is viewable if its MIME type + # is either text/.* or application/vnd.mozilla.*. + # !!! Yuck, what an ugly hack. Fix it! + my $isviewable = ( $mimetype =~ /^(text|image|application\/vnd\.mozilla\.)/ ); + + # Retrieve a list of status flags that have been set on the attachment. + my %statuses; + SendSQL("SELECT id, name + FROM attachstatuses JOIN attachstatusdefs + WHERE attachstatuses.statusid = attachstatusdefs.id + AND attach_id = $::FORM{'id'}"); + while ( my ($id, $name) = FetchSQLData() ) + { + $statuses{$id} = $name; + } + + # Retrieve a list of statuses for this bug's product, and build an array + # of hashes in which each hash is a status flag record. + # ???: Move this into versioncache or its own routine? + my @statusdefs; + SendSQL("SELECT id, name + FROM attachstatusdefs, bugs + WHERE bug_id = $bugid + AND attachstatusdefs.product = bugs.product + ORDER BY sortkey"); + while ( MoreSQLData() ) + { + my ($id, $name) = FetchSQLData(); + push @statusdefs, { 'id' => $id , 'name' => $name }; + } + + # Retrieve a list of attachments for this bug as well as a summary of the bug + # to use in a navigation bar across the top of the screen. + SendSQL("SELECT attach_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id"); + my @bugattachments; + push(@bugattachments, FetchSQLData()) while (MoreSQLData()); + SendSQL("SELECT short_desc FROM bugs WHERE bug_id = $bugid"); + my ($bugsummary) = FetchSQLData(); + + # Define the variables and functions that will be passed to the UI template. + $vars->{'attachid'} = $::FORM{'id'}; + $vars->{'description'} = $description; + $vars->{'mimetype'} = $mimetype; + $vars->{'bugid'} = $bugid; + $vars->{'bugsummary'} = $bugsummary; + $vars->{'ispatch'} = $ispatch; + $vars->{'isobsolete'} = $isobsolete; + $vars->{'isviewable'} = $isviewable; + $vars->{'statuses'} = \%statuses; + $vars->{'statusdefs'} = \@statusdefs; + $vars->{'attachments'} = \@bugattachments; + + # Return the appropriate HTTP response headers. + print "Content-Type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/edit.atml", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; +} + + +sub update +{ + # Update an attachment record. + + # Get the bug ID for the bug to which this attachment is attached. + SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); + my $bugid = FetchSQLData() + || DisplayError("Cannot figure out bug number.") + && exit; + + # Lock database tables in preparation for updating the attachment. + SendSQL("LOCK TABLES attachments WRITE , attachstatuses WRITE , + attachstatusdefs READ , fielddefs READ , bugs_activity WRITE"); + + # Get a copy of the attachment record before we make changes + # so we can record those changes in the activity table. + SendSQL("SELECT description, mimetype, ispatch, isobsolete + FROM attachments WHERE attach_id = $::FORM{'id'}"); + my ($olddescription, $oldmimetype, $oldispatch, $oldisobsolete) = FetchSQLData(); + + # Get the list of old status flags. + SendSQL("SELECT attachstatusdefs.name + FROM attachments, attachstatuses, attachstatusdefs + WHERE attachments.attach_id = $::FORM{'id'} + AND attachments.attach_id = attachstatuses.attach_id + AND attachstatuses.statusid = attachstatusdefs.id + ORDER BY attachstatusdefs.sortkey + "); + my @oldstatuses; + while (MoreSQLData()) { + push(@oldstatuses, FetchSQLData()); + } + my $oldstatuslist = join(', ', @oldstatuses); + + # Update the database with the new status flags. + SendSQL("DELETE FROM attachstatuses WHERE attach_id = $::FORM{'id'}"); + foreach my $statusid (@{$::MFORM{'status'}}) + { + SendSQL("INSERT INTO attachstatuses (attach_id, statusid) VALUES ($::FORM{'id'}, $statusid)"); + } + + # Get the list of new status flags. + SendSQL("SELECT attachstatusdefs.name + FROM attachments, attachstatuses, attachstatusdefs + WHERE attachments.attach_id = $::FORM{'id'} + AND attachments.attach_id = attachstatuses.attach_id + AND attachstatuses.statusid = attachstatusdefs.id + ORDER BY attachstatusdefs.sortkey + "); + my @newstatuses; + while (MoreSQLData()) { + push(@newstatuses, FetchSQLData()); + } + my $newstatuslist = join(', ', @newstatuses); + + # Quote "description" and "mimetype" for use in the SQL UPDATE statement. + my $quoteddescription = SqlQuote($::FORM{'description'}); + my $quotedmimetype = SqlQuote($::FORM{'mimetype'}); + + # Update the attachment record in the database. + # Sets the creation timestamp to itself to avoid it being updated automatically. + SendSQL("UPDATE attachments + SET description = $quoteddescription , + mimetype = $quotedmimetype , + ispatch = $::FORM{'ispatch'} , + isobsolete = $::FORM{'isobsolete'} , + creation_ts = creation_ts + WHERE attach_id = $::FORM{'id'} + "); + + # Record changes in the activity table. + if ($olddescription ne $::FORM{'description'}) { + my $quotedolddescription = SqlQuote($olddescription); + my $fieldid = GetFieldID('attachments.description'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedolddescription, $quoteddescription)"); + } + if ($oldmimetype ne $::FORM{'mimetype'}) { + my $quotedoldmimetype = SqlQuote($oldmimetype); + my $fieldid = GetFieldID('attachments.mimetype'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldmimetype, $quotedmimetype)"); + } + if ($oldispatch ne $::FORM{'ispatch'}) { + my $fieldid = GetFieldID('attachments.ispatch'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldispatch, $::FORM{'ispatch'})"); + } + if ($oldisobsolete ne $::FORM{'isobsolete'}) { + my $fieldid = GetFieldID('attachments.isobsolete'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldisobsolete, $::FORM{'isobsolete'})"); + } + if ($oldstatuslist ne $newstatuslist) { + my ($removed, $added) = DiffStrings($oldstatuslist, $newstatuslist); + my $quotedremoved = SqlQuote($removed); + my $quotedadded = SqlQuote($added); + my $fieldid = GetFieldID('attachstatusdefs.name'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedremoved, $quotedadded)"); + } + + # Unlock all database tables now that we are finished updating the database. + SendSQL("UNLOCK TABLES"); + + # If this installation has enabled the request manager, let the manager know + # an attachment was updated so it can check for requests on that attachment + # and fulfill them. The request manager allows users to request database + # changes of other users and tracks the fulfillment of those requests. When + # an attachment record is updated and the request manager is called, it will + # fulfill those requests that were requested of the user performing the update + # which are requests for the attachment being updated. + #my $requests; + #if (Param('userequestmanager')) + #{ + # use Request; + # # Specify the fieldnames that have been updated. + # my @fieldnames = ('description', 'mimetype', 'status', 'ispatch', 'isobsolete'); + # # Fulfill pending requests. + # $requests = Request::fulfillRequest('attachment', $::FORM{'id'}, @fieldnames); + # $vars->{'requests'} = $requests; + #} + + # If the user submitted a comment while editing the attachment, + # add the comment to the bug. + if ( $::FORM{'comment'} ) + { + # Append a string to the comment to let users know that the comment came from + # the "edit attachment" screen. + my $comment = qq|(From update of attachment $::FORM{'id'})\n| . $::FORM{'comment'}; + + # Get the user's login name since the AppendComment function needs it. + my $who = DBID_to_name($::userid); + # Mention $::userid again so Perl doesn't give me a warning about it. + my $neverused = $::userid; + + # Append the comment to the list of comments in the database. + AppendComment($bugid, $who, $comment); + + } + + # Send mail to let people know the bug has changed. Uses a special syntax + # of the "open" and "exec" commands to capture the output of "processmail", + # which "system" doesn't allow, without running the command through a shell, + # which backticks (``) do. + #system ("./processmail", $bugid , $::userid); + #my $mailresults = `./processmail $bugid $::userid`; + my $mailresults = ''; + open(PMAIL, "-|") or exec('./processmail', $bugid, $::userid); + $mailresults .= $_ while ; + close(PMAIL); + + # Define the variables and functions that will be passed to the UI template. + $vars->{'attachid'} = $::FORM{'id'}; + $vars->{'bugid'} = $bugid; + $vars->{'mailresults'} = $mailresults; + + # Return the appropriate HTTP response headers. + print "Content-Type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/updated.atml", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; + +} -- cgit v1.2.3-65-gdbad From 13ec5d3f1252b76510b6489faad69f4704ada1d7 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Sat, 1 Sep 2001 04:40:33 +0000 Subject: Fix for bug 97764: Fixes errant sending of mail on attachment changes to users who do not want to receive mail about their own changes. Patch by Jake . r=myk@mozilla.org; no second review needed. --- attachment.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index e0b723734..9308b1d73 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -499,7 +499,7 @@ sub update #system ("./processmail", $bugid , $::userid); #my $mailresults = `./processmail $bugid $::userid`; my $mailresults = ''; - open(PMAIL, "-|") or exec('./processmail', $bugid, $::userid); + open(PMAIL, "-|") or exec('./processmail', $bugid, DBID_to_name($::userid)); $mailresults .= $_ while ; close(PMAIL); -- cgit v1.2.3-65-gdbad From 7279d56bac905f63a3091caeeb40299897340c44 Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Thu, 11 Oct 2001 09:17:56 +0000 Subject: Fix for bug 97784: comments in attachment update form are now properly word-wrapped. This is a server-side implementation to do the word-wrapping, which will probably eventually be used in the main comments area on the bug form as well. Patch by Myk Melez r= gerv, justdave --- attachment.cgi | 19 ++++++++++++++++++- template/default/attachment/edit.atml | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 9308b1d73..8fcac0b88 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -478,17 +478,34 @@ sub update # add the comment to the bug. if ( $::FORM{'comment'} ) { + use Text::Wrap; + $Text::Wrap::columns = 80; + $Text::Wrap::huge = 'wrap'; + # Append a string to the comment to let users know that the comment came from # the "edit attachment" screen. my $comment = qq|(From update of attachment $::FORM{'id'})\n| . $::FORM{'comment'}; + my $wrappedcomment = ""; + foreach my $line (split(/\r\n|\r|\n/, $comment)) + { + if ( $line =~ /^>/ ) + { + $wrappedcomment .= $line . "\n"; + } + else + { + $wrappedcomment .= wrap('', '', $line) . "\n"; + } + } + # Get the user's login name since the AppendComment function needs it. my $who = DBID_to_name($::userid); # Mention $::userid again so Perl doesn't give me a warning about it. my $neverused = $::userid; # Append the comment to the list of comments in the database. - AppendComment($bugid, $who, $comment); + AppendComment($bugid, $who, $wrappedcomment); } diff --git a/template/default/attachment/edit.atml b/template/default/attachment/edit.atml index e1fc3e035..5981a692f 100755 --- a/template/default/attachment/edit.atml +++ b/template/default/attachment/edit.atml @@ -26,7 +26,7 @@ Description:
-
+
MIME Type:

@@ -44,7 +44,7 @@
Comment (on the bug):
-
+
-- cgit v1.2.3-65-gdbad From 2b77f7466f51a1248e7cb8af45f41d812ddef8f8 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Wed, 31 Oct 2001 07:42:21 +0000 Subject: Fix for bug 98602: re-implementation of "create attachment" page. Patch by Myk Melez . r=gerv@mozilla.org,jake@acutex.net --- Attachment.pm | 5 +- Bugzilla/Attachment.pm | 5 +- CGI.pl | 76 +++++++----- attachment.cgi | 323 ++++++++++++++++++++++++++++++++++++++++++++----- checksetup.pl | 1 + defparams.pl | 24 ++++ 6 files changed, 363 insertions(+), 71 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 15ff3a1f3..9f46fd083 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -72,16 +72,13 @@ sub list my @attachments = (); while (&::MoreSQLData()) { my %a; - ($a{'attachid'}, $a{'date'}, $a{'mimetype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = &::FetchSQLData(); + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = &::FetchSQLData(); # Format the attachment's creation/modification date into something readable. if ($a{'date'} =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { $a{'date'} = "$3/$4/$2 $5:$6"; } - # Quote HTML characters (&<>) in the description so they display correctly. - $a{'description'} = &::value_quote($a{'description'}); - # Retrieve a list of status flags that have been set on the attachment. &::PushGlobalSQLState(); &::SendSQL(" diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 15ff3a1f3..9f46fd083 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -72,16 +72,13 @@ sub list my @attachments = (); while (&::MoreSQLData()) { my %a; - ($a{'attachid'}, $a{'date'}, $a{'mimetype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = &::FetchSQLData(); + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = &::FetchSQLData(); # Format the attachment's creation/modification date into something readable. if ($a{'date'} =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { $a{'date'} = "$3/$4/$2 $5:$6"; } - # Quote HTML characters (&<>) in the description so they display correctly. - $a{'description'} = &::value_quote($a{'description'}); - # Retrieve a list of status flags that have been set on the attachment. &::PushGlobalSQLState(); &::SendSQL(" diff --git a/CGI.pl b/CGI.pl index 793e2eb07..593079c4a 100644 --- a/CGI.pl +++ b/CGI.pl @@ -42,7 +42,6 @@ use strict; sub CGI_pl_sillyness { my $zz; - $zz = %::FILENAME; $zz = %::MFORM; $zz = %::dontchange; } @@ -148,46 +147,63 @@ sub ProcessFormFields { sub ProcessMultipartFormFields { - my ($boundary) = (@_); - $boundary =~ s/^-*//; - my $remaining = $ENV{"CONTENT_LENGTH"}; + my ($boundary) = @_; + + # Initialize variables that store whether or not we are parsing a header, + # the name of the part we are parsing, and its value (which is incomplete + # until we finish parsing the part). my $inheader = 1; - my $itemname = ""; -# open(DEBUG, ">debug") || die "Can't open debugging thing"; -# print DEBUG "Boundary is '$boundary'\n"; + my $fieldname = ""; + my $fieldvalue = ""; + + # Read the input stream line by line and parse it into a series of parts, + # each one containing a single form field and its value and each one + # separated from the next by the value of $boundary. + my $remaining = $ENV{"CONTENT_LENGTH"}; while ($remaining > 0 && ($_ = )) { $remaining -= length($_); -# print DEBUG "< $_"; + + # If the current input line is a boundary line, save the previous + # form value and reset the storage variables. if ($_ =~ m/^-*$boundary/) { -# print DEBUG "Entered header\n"; - $inheader = 1; - $itemname = ""; - next; - } + if ( $fieldname ) { + chomp($fieldvalue); + $fieldvalue =~ s/\r$//; + if ( defined $::FORM{$fieldname} ) { + $::FORM{$fieldname} .= $fieldvalue; + push @{$::MFORM{$fieldname}}, $fieldvalue; + } else { + $::FORM{$fieldname} = $fieldvalue; + $::MFORM{$fieldname} = [$fieldvalue]; + } + } - if ($inheader) { + $inheader = 1; + $fieldname = ""; + $fieldvalue = ""; + + # If the current input line is a header line, look for a blank line + # (meaning the end of the headers), a Content-Disposition header + # (containing the field name and, for uploaded file parts, the file + # name), or a Content-Type header (containing the content type for + # file parts). + } elsif ( $inheader ) { if (m/^\s*$/) { $inheader = 0; -# print DEBUG "left header\n"; - $::FORM{$itemname} = ""; - } - if (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) { - $itemname = $1; -# print DEBUG "Found itemname $itemname\n"; + } elsif (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) { + $fieldname = $1; if (m/;\s*filename\s*=\s*"([^\"]+)"/i) { - $::FILENAME{$itemname} = $1; + $::FILE{$fieldname}->{'filename'} = $1; } + } elsif ( m|^Content-Type:\s*([^/]+/[^\s;]+)|i ) { + $::FILE{$fieldname}->{'contenttype'} = $1; } - - next; + + # If the current input line is neither a boundary line nor a header, + # it must be part of the field value, so append it to the value. + } else { + $fieldvalue .= $_; } - $::FORM{$itemname} .= $_; - } - delete $::FORM{""}; - # Get rid of trailing newlines. - foreach my $i (keys %::FORM) { - chomp($::FORM{$i}); - $::FORM{$i} =~ s/\r$//; } } diff --git a/attachment.cgi b/attachment.cgi index 8fcac0b88..2f48c0805 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -89,6 +89,22 @@ elsif ($action eq "viewall") ValidateBugID($::FORM{'bugid'}); viewall(); } +elsif ($action eq "enter") +{ + ValidateBugID($::FORM{'bugid'}); + enter(); +} +elsif ($action eq "insert") +{ + ValidateBugID($::FORM{'bugid'}); + validateFilename(); + validateData(); + validateDescription(); + validateIsPatch(); + validateContentType() unless $::FORM{'ispatch'}; + validateObsolete() if $::FORM{'obsolete'}; + insert(); +} elsif ($action eq "edit") { validateID(); @@ -102,8 +118,8 @@ elsif ($action eq "update") && exit; validateID(); validateDescription(); - validateMIMEType(); validateIsPatch(); + validateContentType() unless $::FORM{'ispatch'}; validateIsObsolete(); validateStatuses(); update(); @@ -146,21 +162,68 @@ sub validateDescription && exit; } -sub validateMIMEType -{ - $::FORM{'mimetype'} =~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ - || DisplayError("You must enter a valid MIME type of the form foo/bar - where foo is either application, audio, image, message, - model, multipart, text, or video.") - && exit; -} - sub validateIsPatch { # Set the ispatch flag to zero if it is undefined, since the UI uses # an HTML checkbox to represent this flag, and unchecked HTML checkboxes # do not get sent in HTML requests. $::FORM{'ispatch'} = $::FORM{'ispatch'} ? 1 : 0; + + # Set the content type to text/plain if the attachment is a patch. + $::FORM{'contenttype'} = "text/plain" if $::FORM{'ispatch'}; +} + +sub validateContentType +{ + if (!$::FORM{'contenttypemethod'}) + { + DisplayError("You must choose a method for determining the content type, + either auto-detect, select from list, or enter + manually."); + exit; + } + elsif ($::FORM{'contenttypemethod'} eq 'autodetect') + { + # The user asked us to auto-detect the content type, so use the type + # specified in the HTTP request headers. + if ( !$::FILE{'data'}->{'contenttype'} ) + { + DisplayError("You asked Bugzilla to auto-detect the content type, but + your browser did not specify a content type when uploading the file, + so you must enter a content type manually."); + exit; + } + $::FORM{'contenttype'} = $::FILE{'data'}->{'contenttype'}; + } + elsif ($::FORM{'contenttypemethod'} eq 'list') + { + # The user selected a content type from the list, so use their selection. + $::FORM{'contenttype'} = $::FORM{'contenttypeselection'}; + } + elsif ($::FORM{'contenttypemethod'} eq 'manual') + { + # The user entered a content type manually, so use their entry. + $::FORM{'contenttype'} = $::FORM{'contenttypeentry'}; + } + else + { + my $htmlcontenttypemethod = html_quote($::FORM{'contenttypemethod'}); + DisplayError("Your form submission got corrupted somehow. The content + method field, which specifies how the content type gets determined, + should have been either autodetect, list, + or manual, but was instead $htmlcontenttypemethod."); + exit; + } + + if ( $::FORM{'contenttype'} !~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ ) + { + my $htmlcontenttype = html_quote($::FORM{'contenttype'}); + DisplayError("The content type $htmlcontenttype is invalid. + Valid types must be of the form foo/bar where foo + is either application, audio, image, message, model, multipart, + text, or video."); + exit; + } } sub validateIsObsolete @@ -193,6 +256,95 @@ sub validateStatuses } } +sub validateData +{ + $::FORM{'data'} + || DisplayError("The file you are trying to attach is empty!") + && exit; + + my $len = length($::FORM{'data'}); + + my $maxpatchsize = Param('maxpatchsize'); + my $maxattachmentsize = Param('maxattachmentsize'); + + # Makes sure the attachment does not exceed either the "maxpatchsize" or + # the "maxattachmentsize" parameter. + if ( $::FORM{'ispatch'} && $maxpatchsize && $len > $maxpatchsize*1024 ) + { + my $lenkb = sprintf("%.0f", $len/1024); + DisplayError("The file you are trying to attach is ${lenkb} kilobytes (KB) in size. + Patches cannot be more than ${maxpatchsize}KB in size. + Try breaking your patch into several pieces."); + exit; + } elsif ( !$::FORM{'ispatch'} && $maxattachmentsize && $len > $maxattachmentsize*1024 ) { + my $lenkb = sprintf("%.0f", $len/1024); + DisplayError("The file you are trying to attach is ${lenkb} kilobytes (KB) in size. + Non-patch attachments cannot be more than ${maxattachmentsize}KB. + If your attachment is an image, try converting it to a compressable + format like JPG or PNG, or put it elsewhere on the web and + link to it from the bug's URL field or in a comment on the bug."); + exit; + } +} + +sub validateFilename +{ + defined $::FILE{'data'} + || DisplayError("You did not specify a file to attach.") + && exit; +} + +sub validateObsolete +{ + # When a user creates an attachment, they can request that one or more + # existing attachments be made obsolete. This function makes sure they + # are authorized to make changes to attachments and that the IDs of the + # attachments they selected for obsoletion are all valid. + UserInGroup("editbugs") + || DisplayError("You must be authorized to make changes to attachments + to make attachments obsolete when creating a new attachment.") + && exit; + + # Make sure the attachment id is valid and the user has permissions to view + # the bug to which it is attached. + foreach my $attachid (@{$::MFORM{'obsolete'}}) { + $attachid =~ /^[1-9][0-9]*$/ + || DisplayError("The attachment number of one of the attachments + you wanted to obsolete is invalid.") + && exit; + + SendSQL("SELECT bug_id, isobsolete, description + FROM attachments WHERE attach_id = $attachid"); + + # Make sure the attachment exists in the database. + MoreSQLData() + || DisplayError("Attachment #$attachid does not exist.") + && exit; + + my ($bugid, $isobsolete, $description) = FetchSQLData(); + + # Make sure the user is authorized to access this attachment's bug. + ValidateBugID($bugid); + + if ($bugid != $::FORM{'bugid'}) + { + $description = html_quote($description); + DisplayError("Attachment #$attachid ($description) is attached + to bug #$bugid, but you tried to flag it as obsolete while + creating a new attachment to bug #$::FORM{'bugid'}."); + exit; + } + + if ( $isobsolete ) + { + $description = html_quote($description); + DisplayError("Attachment #$attachid ($description) is already obsolete."); + exit; + } + } + +} + ################################################################################ # Functions ################################################################################ @@ -201,12 +353,12 @@ sub view { # Display an attachment. - # Retrieve the attachment content and its MIME type from the database. + # Retrieve the attachment content and its content type from the database. SendSQL("SELECT mimetype, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($mimetype, $thedata) = FetchSQLData(); + my ($contenttype, $thedata) = FetchSQLData(); # Return the appropriate HTTP response headers. - print "Content-Type: $mimetype\n\n"; + print "Content-Type: $contenttype\n\n"; print $thedata; } @@ -221,9 +373,10 @@ sub viewall SendSQL("SELECT attach_id, creation_ts, mimetype, description, ispatch, isobsolete FROM attachments WHERE bug_id = $::FORM{'bugid'} ORDER BY attach_id"); my @attachments; # the attachments array - while ( MoreSQLData() ) { + while (MoreSQLData()) + { my %a; # the attachment hash - ($a{'attachid'}, $a{'date'}, $a{'mimetype'}, + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = FetchSQLData(); # Format the attachment's creation/modification date into something readable. @@ -231,15 +384,11 @@ sub viewall $a{'date'} = "$3/$4/$2 $5:$6"; } - # Quote HTML characters (&<>) in the description and MIME Type. - $a{'description'} = value_quote($a{'description'}); - $a{'mimetype'} = value_quote($a{'mimetype'}); - # Flag attachments as to whether or not they can be viewed (as opposed to # being downloaded). Currently I decide they are viewable if their MIME type # is either text/*, image/*, or application/vnd.mozilla.*. # !!! Yuck, what an ugly hack. Fix it! - $a{'isviewable'} = ( $a{'mimetype'} =~ /^(text|image|application\/vnd\.mozilla\.)/ ); + $a{'isviewable'} = ( $a{'contenttype'} =~ /^(text|image|application\/vnd\.mozilla\.)/ ); # Retrieve a list of status flags that have been set on the attachment. PushGlobalSQLState(); @@ -276,23 +425,131 @@ sub viewall } +sub enter +{ + # Display a form for entering a new attachment. + + # Retrieve the attachments from the database and write them into an array + # of hashes where each hash represents one attachment. + SendSQL("SELECT attach_id, description + FROM attachments + WHERE bug_id = $::FORM{'bugid'} + AND isobsolete = 0 + ORDER BY attach_id"); + my @attachments; # the attachments array + while ( MoreSQLData() ) { + my %a; # the attachment hash + ($a{'id'}, $a{'description'}) = FetchSQLData(); + + # Add the hash representing the attachment to the array of attachments. + push @attachments, \%a; + } + + # Retrieve the bug summary for displaying on screen. + SendSQL("SELECT short_desc FROM bugs WHERE bug_id = $::FORM{'bugid'}"); + my ($bugsummary) = FetchSQLData(); + + # Define the variables and functions that will be passed to the UI template. + $vars->{'bugid'} = $::FORM{'bugid'}; + $vars->{'bugsummary'} = $bugsummary; + $vars->{'attachments'} = \@attachments; + + # Return the appropriate HTTP response headers. + print "Content-Type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/enter.atml", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; +} + + +sub insert +{ + # Insert a new attachment into the database. + + # Escape characters in strings that will be used in SQL statements. + my $filename = SqlQuote($::FILE{'data'}->{'filename'}); + my $description = SqlQuote($::FORM{'description'}); + my $contenttype = SqlQuote($::FORM{'contenttype'}); + my $submitterid = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'}); + my $thedata = SqlQuote($::FORM{'data'}); + + # Insert the attachment into the database. + SendSQL("INSERT INTO attachments (bug_id, filename, description, mimetype, ispatch, submitter_id, thedata) + VALUES ($::FORM{'bugid'}, $filename, $description, $contenttype, $::FORM{'ispatch'}, $submitterid, $thedata)"); + + # Retrieve the ID of the newly created attachment record. + SendSQL("SELECT LAST_INSERT_ID()"); + my $attachid = FetchOneColumn(); + + # Insert a comment about the new attachment into the database. + my $comment = "Created an attachment (id=$attachid): $::FORM{'description'}\n"; + $comment .= ("\n" . $::FORM{'comment'}) if $::FORM{'comment'}; + + use Text::Wrap; + $Text::Wrap::columns = 80; + $Text::Wrap::huge = 'overflow'; + $comment = Text::Wrap::wrap('', '', $comment); + + AppendComment($::FORM{'bugid'}, + $::COOKIE{"Bugzilla_login"}, + $comment); + + # Make existing attachments obsolete. + my $fieldid = GetFieldID('attachments.isobsolete'); + foreach my $attachid (@{$::MFORM{'obsolete'}}) { + SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $attachid"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($::FORM{'bugid'}, $attachid, $submitterid, NOW(), $fieldid, '0', '1')"); + } + + # Send mail to let people know the attachment has been created. Uses a + # special syntax of the "open" and "exec" commands to capture the output of + # "processmail", which "system" doesn't allow, without running the command + # through a shell, which backticks (``) do. + #system ("./processmail", $bugid , $::userid); + #my $mailresults = `./processmail $bugid $::userid`; + my $mailresults = ''; + open(PMAIL, "-|") or exec('./processmail', $::FORM{'bugid'}, $::COOKIE{'Bugzilla_login'}); + $mailresults .= $_ while ; + close(PMAIL); + + # Define the variables and functions that will be passed to the UI template. + $vars->{'bugid'} = $::FORM{'bugid'}; + $vars->{'attachid'} = $attachid; + $vars->{'description'} = $description; + $vars->{'mailresults'} = $mailresults; + $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; + $vars->{'contenttype'} = $::FORM{'contenttype'}; + + # Return the appropriate HTTP response headers. + print "Content-Type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/created.atml", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; +} + + sub edit { # Edit an attachment record. Users with "editbugs" privileges can edit the - # attachment's description, MIME type, ispatch and isobsolete flags, and + # attachment's description, content type, ispatch and isobsolete flags, and # statuses, and they can also submit a comment that appears in the bug. # Users cannot edit the content of the attachment itself. # Retrieve the attachment from the database. SendSQL("SELECT description, mimetype, bug_id, ispatch, isobsolete FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($description, $mimetype, $bugid, $ispatch, $isobsolete) = FetchSQLData(); + my ($description, $contenttype, $bugid, $ispatch, $isobsolete) = FetchSQLData(); # Flag attachment as to whether or not it can be viewed (as opposed to - # being downloaded). Currently I decide it is viewable if its MIME type - # is either text/.* or application/vnd.mozilla.*. + # being downloaded). Currently I decide it is viewable if its content + # type is either text/.* or application/vnd.mozilla.*. # !!! Yuck, what an ugly hack. Fix it! - my $isviewable = ( $mimetype =~ /^(text|image|application\/vnd\.mozilla\.)/ ); + my $isviewable = ( $contenttype =~ /^(text|image|application\/vnd\.mozilla\.)/ ); # Retrieve a list of status flags that have been set on the attachment. my %statuses; @@ -331,7 +588,7 @@ sub edit # Define the variables and functions that will be passed to the UI template. $vars->{'attachid'} = $::FORM{'id'}; $vars->{'description'} = $description; - $vars->{'mimetype'} = $mimetype; + $vars->{'contenttype'} = $contenttype; $vars->{'bugid'} = $bugid; $vars->{'bugsummary'} = $bugsummary; $vars->{'ispatch'} = $ispatch; @@ -369,7 +626,7 @@ sub update # so we can record those changes in the activity table. SendSQL("SELECT description, mimetype, ispatch, isobsolete FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($olddescription, $oldmimetype, $oldispatch, $oldisobsolete) = FetchSQLData(); + my ($olddescription, $oldcontenttype, $oldispatch, $oldisobsolete) = FetchSQLData(); # Get the list of old status flags. SendSQL("SELECT attachstatusdefs.name @@ -406,15 +663,15 @@ sub update } my $newstatuslist = join(', ', @newstatuses); - # Quote "description" and "mimetype" for use in the SQL UPDATE statement. + # Quote the description and content type for use in the SQL UPDATE statement. my $quoteddescription = SqlQuote($::FORM{'description'}); - my $quotedmimetype = SqlQuote($::FORM{'mimetype'}); + my $quotedcontenttype = SqlQuote($::FORM{'contenttype'}); # Update the attachment record in the database. # Sets the creation timestamp to itself to avoid it being updated automatically. SendSQL("UPDATE attachments SET description = $quoteddescription , - mimetype = $quotedmimetype , + mimetype = $quotedcontenttype , ispatch = $::FORM{'ispatch'} , isobsolete = $::FORM{'isobsolete'} , creation_ts = creation_ts @@ -428,11 +685,11 @@ sub update SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedolddescription, $quoteddescription)"); } - if ($oldmimetype ne $::FORM{'mimetype'}) { - my $quotedoldmimetype = SqlQuote($oldmimetype); + if ($oldcontenttype ne $::FORM{'contenttype'}) { + my $quotedoldcontenttype = SqlQuote($oldcontenttype); my $fieldid = GetFieldID('attachments.mimetype'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldmimetype, $quotedmimetype)"); + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldcontenttype, $quotedcontenttype)"); } if ($oldispatch ne $::FORM{'ispatch'}) { my $fieldid = GetFieldID('attachments.ispatch'); diff --git a/checksetup.pl b/checksetup.pl index 9c4474160..0a3edac79 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -191,6 +191,7 @@ unless (have_vers("DBD::mysql","1.2209")) { push @missing,"DBD::mysql" } unless (have_vers("Date::Parse",0)) { push @missing,"Date::Parse" } unless (have_vers("AppConfig","1.52")) { push @missing,"AppConfig" } unless (have_vers("Template","2.01")) { push @missing,"Template" } +unless (have_vers("Text::Wrap","2001.0131")) { push @missing,"Text::Wrap" } # If CGI::Carp was loaded successfully for version checking, it changes the # die and warn handlers, we don't want them changed, so we need to stash the diff --git a/defparams.pl b/defparams.pl index 58b06bec6..76fb77777 100644 --- a/defparams.pl +++ b/defparams.pl @@ -679,4 +679,28 @@ DefParam("useattachmenttracker", "b", 0); +# The maximum size (in bytes) for patches and non-patch attachments. +# The default limit is 1000KB, which is 24KB less than mysql's default +# maximum packet size (which determines how much data can be sent in a +# single mysql packet and thus how much data can be inserted into the +# database) to provide breathing space for the data in other fields of +# the attachment record as well as any mysql packet overhead (I don't +# know of any, but I suspect there may be some.) + +DefParam("maxpatchsize", + "The maximum size (in kilobytes) of patches. Bugzilla will not + accept patches greater than this number of kilobytes in size. + To accept patches of any size (subject to the limitations of + your server software), set this value to zero." , + "t", + '1000'); + +DefParam("maxattachmentsize" , + "The maximum size (in kilobytes) of non-patch attachments. Bugzilla + will not accept attachments greater than this number of kilobytes + in size. To accept attachments of any size (subject to the + limitations of your server software), set this value to zero." , + "t" , + '1000'); + 1; -- cgit v1.2.3-65-gdbad From 8f6ab9204d8aa76613475efb18bccbfaf86e0525 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Fri, 9 Nov 2001 09:11:09 +0000 Subject: Fix for bug 109048: fixes error when creating attachments without logging in. Patch by Myk Melez . r=bbaetz,gerv --- attachment.cgi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 2f48c0805..8792b4aec 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -91,11 +91,13 @@ elsif ($action eq "viewall") } elsif ($action eq "enter") { + confirm_login(); ValidateBugID($::FORM{'bugid'}); enter(); } elsif ($action eq "insert") { + confirm_login(); ValidateBugID($::FORM{'bugid'}); validateFilename(); validateData(); @@ -472,12 +474,11 @@ sub insert my $filename = SqlQuote($::FILE{'data'}->{'filename'}); my $description = SqlQuote($::FORM{'description'}); my $contenttype = SqlQuote($::FORM{'contenttype'}); - my $submitterid = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'}); my $thedata = SqlQuote($::FORM{'data'}); # Insert the attachment into the database. SendSQL("INSERT INTO attachments (bug_id, filename, description, mimetype, ispatch, submitter_id, thedata) - VALUES ($::FORM{'bugid'}, $filename, $description, $contenttype, $::FORM{'ispatch'}, $submitterid, $thedata)"); + VALUES ($::FORM{'bugid'}, $filename, $description, $contenttype, $::FORM{'ispatch'}, $::userid, $thedata)"); # Retrieve the ID of the newly created attachment record. SendSQL("SELECT LAST_INSERT_ID()"); @@ -501,7 +502,7 @@ sub insert foreach my $attachid (@{$::MFORM{'obsolete'}}) { SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $attachid"); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($::FORM{'bugid'}, $attachid, $submitterid, NOW(), $fieldid, '0', '1')"); + VALUES ($::FORM{'bugid'}, $attachid, $::userid, NOW(), $fieldid, '0', '1')"); } # Send mail to let people know the attachment has been created. Uses a -- cgit v1.2.3-65-gdbad From b2bd185ee6b0b796c31d7c3192a166149004881c Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Tue, 20 Nov 2001 10:59:55 +0000 Subject: Fix for bug 109240: fixes the regression that changed the way comments regarding attachment creation are formatted. Patch by Jake . r=myk@mozilla.org, no second review needed. --- attachment.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 8792b4aec..d908a5ebd 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -485,7 +485,7 @@ sub insert my $attachid = FetchOneColumn(); # Insert a comment about the new attachment into the database. - my $comment = "Created an attachment (id=$attachid): $::FORM{'description'}\n"; + my $comment = "Created an attachment (id=$attachid)\n$::FORM{'description'}\n"; $comment .= ("\n" . $::FORM{'comment'}) if $::FORM{'comment'}; use Text::Wrap; -- cgit v1.2.3-65-gdbad From 07f94a610c1ef871d5c636a6f9f030e084cdc7aa Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Thu, 22 Nov 2001 14:12:36 +0000 Subject: Bug 104261 - incorrect template paths. r=myk, ddk. --- Attachment.pm | 2 +- Bugzilla/Attachment.pm | 2 +- attachment.cgi | 2 +- editattachstatuses.cgi | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 9f46fd083..1ba194d77 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -42,7 +42,7 @@ use Template; my $template = Template->new( { # Colon-separated list of directories containing templates. - INCLUDE_PATH => 'template/default' , + INCLUDE_PATH => 'template/custom:template/default' , # Allow templates to be specified with relative paths. RELATIVE => 1 } diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 9f46fd083..1ba194d77 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -42,7 +42,7 @@ use Template; my $template = Template->new( { # Colon-separated list of directories containing templates. - INCLUDE_PATH => 'template/default' , + INCLUDE_PATH => 'template/custom:template/default' , # Allow templates to be specified with relative paths. RELATIVE => 1 } diff --git a/attachment.cgi b/attachment.cgi index d908a5ebd..e5c3f52fe 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -45,7 +45,7 @@ use Template; my $template = Template->new( { # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/default" , + INCLUDE_PATH => "template/custom:template/default" , # Allow templates to be specified with relative paths. RELATIVE => 1 } diff --git a/editattachstatuses.cgi b/editattachstatuses.cgi index 9feb9a765..6596a79c9 100755 --- a/editattachstatuses.cgi +++ b/editattachstatuses.cgi @@ -45,7 +45,7 @@ use Template; my $template = Template->new( { # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/default" , + INCLUDE_PATH => "template/custom:template/default" , # Allow templates to be specified with relative paths. RELATIVE => 1 } -- cgit v1.2.3-65-gdbad From 4e6767d4c3d1b0b583f4ec076992345545294748 Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Sun, 20 Jan 2002 09:44:34 +0000 Subject: Fix for bug 108982: enable taint mode for all user-facing CGI files. Patch by Brad Baetz r= jake, justdave --- Attachment.pm | 1 - Bug.pm | 11 ++++++----- Bugzilla/Attachment.pm | 1 - Bugzilla/Bug.pm | 11 ++++++----- Bugzilla/Token.pm | 1 - CGI.pl | 19 +++++++++++++------ Token.pm | 1 - attachment.cgi | 8 +++++--- buglist.cgi | 20 ++++++++++++++++++-- bugzilla.dtd | 2 +- changepassword.cgi | 2 +- colchange.cgi | 4 +++- createaccount.cgi | 4 +++- createattachment.cgi | 4 +++- describecomponents.cgi | 4 +++- describekeywords.cgi | 4 +++- doeditparams.cgi | 4 +++- doeditvotes.cgi | 9 ++++----- duplicates.cgi | 5 ++++- enter_bug.cgi | 4 +++- globals.pl | 19 +++++++++++++++++++ long_list.cgi | 4 +++- move.pl | 5 ++++- new_comment.cgi | 2 +- post_bug.cgi | 4 +++- process_bug.cgi | 32 ++++++++++++++++++++++---------- queryhelp.cgi | 4 +++- quips.cgi | 4 +++- relogin.cgi | 4 +++- reports.cgi | 4 +++- sanitycheck.cgi | 4 +++- show_activity.cgi | 4 +++- show_bug.cgi | 4 +++- showattachment.cgi | 6 ++++-- showdependencygraph.cgi | 8 +++++++- showvotes.cgi | 6 ++++-- t/002goodperl.t | 33 ++++++++++++++++++++++++++++++--- token.cgi | 4 +++- userprefs.cgi | 4 +++- xml.cgi | 7 +++++-- 40 files changed, 208 insertions(+), 73 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 1ba194d77..acfa52f63 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -1,4 +1,3 @@ -#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/Bug.pm b/Bug.pm index 670b274eb..96ff0b8af 100755 --- a/Bug.pm +++ b/Bug.pm @@ -1,4 +1,3 @@ -#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -75,10 +74,12 @@ sub initBug { my $self = shift(); my ($bug_id, $user_id) = (@_); - - if ( (! defined $bug_id) || (!$bug_id) ) { - # no bug number given - return {}; + my $old_bug_id = $bug_id; + if ((! defined $bug_id) || (!$bug_id) || (!&::detaint_natural($bug_id))) { + # no bug number given + $self->{'bug_id'} = $old_bug_id; + $self->{'error'} = "InvalidBugId"; + return $self; } # default userid 0, or get DBID if you used an email address diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 1ba194d77..acfa52f63 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -1,4 +1,3 @@ -#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 670b274eb..96ff0b8af 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -1,4 +1,3 @@ -#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -75,10 +74,12 @@ sub initBug { my $self = shift(); my ($bug_id, $user_id) = (@_); - - if ( (! defined $bug_id) || (!$bug_id) ) { - # no bug number given - return {}; + my $old_bug_id = $bug_id; + if ((! defined $bug_id) || (!$bug_id) || (!&::detaint_natural($bug_id))) { + # no bug number given + $self->{'bug_id'} = $old_bug_id; + $self->{'error'} = "InvalidBugId"; + return $self; } # default userid 0, or get DBID if you used an email address diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 185884c98..f2c5fbde7 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -1,4 +1,3 @@ -#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/CGI.pl b/CGI.pl index e245c1db4..16ec7f2d8 100644 --- a/CGI.pl +++ b/CGI.pl @@ -93,6 +93,9 @@ sub url_quote { sub ParseUrlString { + # We don't want to detaint the user supplied data... + use re 'taint'; + my ($buffer, $f, $m) = (@_); undef %$f; undef %$m; @@ -118,6 +121,7 @@ sub ParseUrlString { $name = $item; $value = ""; } + if ($value ne "") { if (defined $f->{$name}) { $f->{$name} .= $value; @@ -141,7 +145,6 @@ sub ParseUrlString { } } - sub ProcessFormFields { my ($buffer) = (@_); return ParseUrlString($buffer, \%::FORM, \%::MFORM); @@ -259,18 +262,18 @@ sub ValidateBugID { # Validates and verifies a bug ID, making sure the number is a # positive integer, that it represents an existing bug in the # database, and that the user is authorized to access that bug. + # We detaint the number here, too - my ($id) = @_; - - # Make sure the bug number is a positive integer. - # Whitespace can be ignored because the SQL server will ignore it. - $id =~ /^\s*([1-9][0-9]*)\s*$/ + $_[0] = trim($_[0]); # Allow whitespace arround the number + detaint_natural($_[0]) || DisplayError("The bug number is invalid. If you are trying to use " . "QuickSearch, you need to enable JavaScript in your " . "browser. To help us fix this limitation, look " . "here.") && exit; + my ($id) = @_; + # Get the values of the usergroupset and userid global variables # and write them to local variables for use within this function, # setting those local variables to the default value of zero if @@ -685,6 +688,8 @@ sub quietly_check_login() { $::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case # is in # canonical form. + # We've just verified that this is ok + detaint_natural($::COOKIE{"Bugzilla_logincookie"}); } else { $::disabledreason = $disabledtext; } @@ -1430,6 +1435,8 @@ if (defined $ENV{"REQUEST_METHOD"}) { if (defined $ENV{"HTTP_COOKIE"}) { + # Don't trust anything which came in as a cookie + use re 'taint'; foreach my $pair (split(/;/, $ENV{"HTTP_COOKIE"})) { $pair = trim($pair); if ($pair =~ /^([^=]*)=(.*)$/) { diff --git a/Token.pm b/Token.pm index 185884c98..f2c5fbde7 100644 --- a/Token.pm +++ b/Token.pm @@ -1,4 +1,3 @@ -#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/attachment.cgi b/attachment.cgi index e5c3f52fe..5996aa86d 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -29,6 +29,8 @@ use diagnostics; use strict; +use lib qw(.); + # Include the Bugzilla CGI and general utility library. require "CGI.pl"; @@ -139,10 +141,10 @@ exit; sub validateID { - # Validate the value of the "id" form field, which must contain a positive + # Validate the value of the "id" form field, which must contain an # integer that is the ID of an existing attachment. - $::FORM{'id'} =~ /^[1-9][0-9]*$/ + detaint_natural($::FORM{'id'}) || DisplayError("You did not enter a valid attachment number.") && exit; diff --git a/buglist.cgi b/buglist.cgi index d74563f25..ce67f648e 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -26,6 +26,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; use Date::Parse; @@ -783,6 +785,11 @@ sub GenerateSQL { die "Internal error: $errstr" if $chart < 0; return Error($errstr); } + + # This is either from the internal chart (in which case we + # already know about it), or it was in %chartfields, so it is + # a valid field name, which means that its ok. + trick_taint($f); $q = SqlQuote($v); my $func; $term = undef; @@ -1067,7 +1074,15 @@ my @fields = ("bugs.bug_id", "bugs.groupset"); foreach my $c (@collist) { if (exists $::needquote{$c}) { - push(@fields, "$::key{$c}"); + # The value we are actually using is $::key{$c}, which was created + # using the DefCol() function earlier. We test for the existance + # of $::needsquote{$c} to find out if $c is a legitimate key in the + # hashes that were defined by DefCol(). If $::needsquote{$c} exists, + # then $c is valid and we can use it to look up our key. + # If it doesn't exist, then we know the user is screwing with us + # and we'll just skip it. + trick_taint($c); + push(@fields, $::key{$c}); } } @@ -1142,6 +1157,7 @@ if (defined $::FORM{'order'} && $::FORM{'order'} ne "") { } die "Invalid order: $::FORM{'order'}" unless $::FORM{'order'} =~ /^([a-zA-Z0-9_., ]+)$/; + $::FORM{'order'} = $1; # detaint this, since we've checked it # Extra special disgusting hack: if we are ordering by target_milestone, # change it to order by the sortkey of the target_milestone first. diff --git a/bugzilla.dtd b/bugzilla.dtd index a443a953a..21fbb1dab 100644 --- a/bugzilla.dtd +++ b/bugzilla.dtd @@ -8,7 +8,7 @@ priority, version, rep_platform, assigned_to, delta_ts, component, reporter, target_milestone?, bug_severity, creation_ts, qa_contact?, status_whiteboard?, op_sys, short_desc?, keywords*, dependson*, blocks*, cc*, long_desc?, attachment*)> - + diff --git a/changepassword.cgi b/changepassword.cgi index 73ae90350..dafe5c1ae 100755 --- a/changepassword.cgi +++ b/changepassword.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/colchange.cgi b/colchange.cgi index f96559885..89150b5d4 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,6 +23,8 @@ use diagnostics; use strict; +use lib qw(.); + sub sillyness { # shut up "used only once" warnings my $zz = @::legal_keywords; } diff --git a/createaccount.cgi b/createaccount.cgi index 4e5f6d49d..839b81fb3 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -26,6 +26,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; # Shut up misguided -w warnings about "used only once": diff --git a/createattachment.cgi b/createattachment.cgi index d665e4498..ed8308cac 100755 --- a/createattachment.cgi +++ b/createattachment.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; use vars %::COOKIE, %::FILENAME; diff --git a/describecomponents.cgi b/describecomponents.cgi index bf6f94a34..9a2b99cc2 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -25,6 +25,8 @@ use vars %::FORM; use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); diff --git a/describekeywords.cgi b/describekeywords.cgi index 6e23ca63d..3475fb6eb 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,6 +23,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); diff --git a/doeditparams.cgi b/doeditparams.cgi index a67175e91..7390333c0 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,6 +23,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; require "defparams.pl"; diff --git a/doeditvotes.cgi b/doeditvotes.cgi index 94c36b8f4..4d88e13c9 100755 --- a/doeditvotes.cgi +++ b/doeditvotes.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); @@ -67,9 +69,6 @@ if (0 == @buglist) { # minus sign). foreach my $id (@buglist) { ValidateBugID($id); - ($::FORM{$id} =~ /^\d+$/) - || DisplayError("Only use non-negative numbers for your bug votes.") - && exit; } ###################################################################### @@ -144,7 +143,7 @@ while (MoreSQLData()) { } SendSQL("delete from votes where who = $who"); foreach my $id (@buglist) { - if ($::FORM{$id} > 0) { + if (detaint_natural($::FORM{$id}) && $::FORM{$id} > 0) { SendSQL("insert into votes (who, bug_id, count) values ($who, $id, $::FORM{$id})"); } $affected{$id} = 1; diff --git a/duplicates.cgi b/duplicates.cgi index cd2d14c2a..78f29829f 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -27,6 +27,9 @@ use diagnostics; use strict; use CGI "param"; use AnyDBM_File; + +use lib qw(.); + require "globals.pl"; require "CGI.pl"; diff --git a/enter_bug.cgi b/enter_bug.cgi index bd55b7363..f522f89c9 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -35,6 +35,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; # Shut up misguided -w warnings about "used only once". "use vars" just diff --git a/globals.pl b/globals.pl index 87db566c1..36e1f86f1 100644 --- a/globals.pl +++ b/globals.pl @@ -194,8 +194,27 @@ sub SqlLog { } } +# This is from the perlsec page, slightly modifed to remove a warning +# From that page: +# This function makes use of the fact that the presence of +# tainted data anywhere within an expression renders the +# entire expression tainted. +# Don't ask me how it works... +sub is_tainted { + return not eval { my $foo = join('',@_), kill 0; 1; }; +} + sub SendSQL { my ($str, $dontshadow) = (@_); + + # Don't use DBI's taint stuff yet, because: + # a) We don't want out vars to be tainted (yet) + # b) We want to know who called SendSQL... + # Is there a better way to do b? + if (is_tainted($str)) { + die "Attempted to send tainted string to the database"; + } + my $iswrite = ($str =~ /^(INSERT|REPLACE|UPDATE|DELETE)/i); if ($iswrite && !$::dbwritesallowed) { die "Evil code attempted to write stuff to the shadow database."; diff --git a/long_list.cgi b/long_list.cgi index f6182d439..552457b06 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; # Shut up misguided -w warnings about "used only once". "use vars" just diff --git a/move.pl b/move.pl index 37b8cb7ef..9647aee43 100755 --- a/move.pl +++ b/move.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,6 +23,9 @@ use diagnostics; use strict; + +use lib qw(.); + use Bug; require "CGI.pl"; $::lockcount = 0; diff --git a/new_comment.cgi b/new_comment.cgi index 7f428ef80..bed11b472 100755 --- a/new_comment.cgi +++ b/new_comment.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/post_bug.cgi b/post_bug.cgi index b2b843977..361cba848 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -25,6 +25,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; # Shut up misguided -w warnings about "used only once". For some reason, diff --git a/process_bug.cgi b/process_bug.cgi index ac2e239ef..13942ca5f 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -29,6 +29,8 @@ use strict; my $UserInEditGroupSet = -1; my $UserInCanConfirmGroupSet = -1; +use lib qw(.); + require "CGI.pl"; use RelationSet; @@ -42,6 +44,7 @@ use vars %::versions, %::legal_opsys, %::legal_platform, %::legal_priority, + %::settable_resolution, %::target_milestone, %::legal_severity, %::superusergroupset; @@ -58,13 +61,18 @@ my $requiremilestone = 0; # This list will either consist of a single bug number from the "id" # form/URL field or a series of numbers from multiple form/URL fields # named "id_x" where "x" is the bug number. +# For each bug being modified, make sure its ID is a valid bug number +# representing an existing bug that the user is authorized to access. my @idlist; if (defined $::FORM{'id'}) { + ValidateBugID($::FORM{'id'}); push @idlist, $::FORM{'id'}; } else { foreach my $i (keys %::FORM) { if ($i =~ /^id_([1-9][0-9]*)/) { - push @idlist, $1; + my $id = $1; + ValidateBugID($id); + push @idlist, $id; } } } @@ -74,12 +82,6 @@ scalar(@idlist) || DisplayError("You did not select any bugs to modify.") && exit; -# For each bug being modified, make sure its ID is a valid bug number -# representing an existing bug that the user is authorized to access. -foreach my $id (@idlist) { - ValidateBugID($id); -} - # If we are duping bugs, let's also make sure that we can change # the original. This takes care of issue A on bug 96085. if (defined $::FORM{'dup_id'} && $::FORM{'knob'} eq "duplicate") { @@ -538,7 +540,7 @@ sub ChangeResolution { my ($str) = (@_); if ($str ne $::dontchange) { DoComma(); - $::query .= "resolution = '$str'"; + $::query .= "resolution = " . SqlQuote($str); } } @@ -695,6 +697,8 @@ SWITCH: for ($::FORM{'knob'}) { last SWITCH; }; /^resolve$/ && CheckonComment( "resolve" ) && do { + # Check here, because its the only place we require the resolution + CheckFormField(\%::FORM, 'resolution', \@::settable_resolution); ChangeStatus('RESOLVED'); ChangeResolution($::FORM{'resolution'}); last SWITCH; @@ -1030,8 +1034,15 @@ The changes made were: foreach my $i (split('[\s,]+', $::FORM{$target})) { if ($i eq "") { next; + } + my $orig = $i; + if (!detaint_natural($i)) { + PuntTryAgain("$orig is not a legal bug number"); } + + # Don't use CanSeeBug, since we want to keep deps to bugs a + # user can't see SendSQL("select bug_id from bugs where bug_id = " . SqlQuote($i)); my $comp = FetchOneColumn(); @@ -1049,7 +1060,8 @@ The changes made were: my @stack = @{$deps{$target}}; while (@stack) { my $i = shift @stack; - SendSQL("select $target from dependencies where $me = $i"); + SendSQL("select $target from dependencies where $me = " . + SqlQuote($i)); while (MoreSQLData()) { my $t = FetchOneColumn(); if ($t == $id) { diff --git a/queryhelp.cgi b/queryhelp.cgi index 9206bc144..93cbef755 100755 --- a/queryhelp.cgi +++ b/queryhelp.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -27,6 +27,8 @@ use vars %::FORM; use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); diff --git a/quips.cgi b/quips.cgi index 897bf32d0..d6fff6cfa 100755 --- a/quips.cgi +++ b/quips.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; use vars ( %::FORM ); +use lib qw(.); + require "CGI.pl"; print "Content-type: text/html\n\n"; diff --git a/relogin.cgi b/relogin.cgi index 44fae4cc1..091a96e8d 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -25,6 +25,8 @@ use strict; use vars %::COOKIE; +use lib qw(.); + require "CGI.pl"; my $cookiepath = Param("cookiepath"); diff --git a/reports.cgi b/reports.cgi index 861622d73..0bdc062d1 100755 --- a/reports.cgi +++ b/reports.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -41,6 +41,8 @@ use diagnostics; use strict; +use lib qw(.); + eval "use GD"; my $use_gd = $@ ? 0 : 1; eval "use Chart::Lines"; diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 1bf10f071..c85a3668e 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; use vars %::FORM; diff --git a/show_activity.cgi b/show_activity.cgi index fccc21cd4..f8c55f0a1 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); diff --git a/show_bug.cgi b/show_bug.cgi index c5b569a45..28eb66763 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,6 +23,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); diff --git a/showattachment.cgi b/showattachment.cgi index 78143c550..70f5c6d66 100755 --- a/showattachment.cgi +++ b/showattachment.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -24,6 +24,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; if (!defined $::FORM{'attach_id'}) { @@ -43,7 +45,7 @@ ConnectToDatabase(); quietly_check_login(); -if ($::FORM{attach_id} !~ /^[1-9][0-9]*$/) { +if (!detaint_natural($::FORM{attach_id})) { DisplayError("Attachment ID should be numeric."); exit; } diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index a10afb896..4bb90d497 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,6 +23,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); @@ -168,6 +170,10 @@ node [URL="${urlbase}show_bug.cgi?id=\\N", style=filled, color=lightgrey] # Cleanup any old .dot files created from previous runs. my $since = time() - 24 * 60 * 60; foreach my $f (glob("data/webdot/*.dot")) { + # Here we are deleting all old files. All entries are from the + # data/webdot/ directory. Since we're deleting the file (not following + # symlinks), this can't escape to delete anything it shouldn't + trick_taint($f); if (ModTime($f) < $since) { unlink $f; } diff --git a/showvotes.cgi b/showvotes.cgi index a6928bf84..6ed4bb8e4 100755 --- a/showvotes.cgi +++ b/showvotes.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -25,6 +25,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; ConnectToDatabase(); @@ -61,7 +63,7 @@ if (defined $::FORM{'voteon'}) { # Make sure the user ID is a positive integer representing an existing user. if (defined $::FORM{'user'}) { - $::FORM{'user'} =~ /^([1-9][0-9]*)$/ + detaint_natural($::FORM{'user'}) || DisplayError("The user number is invalid.") && exit; SendSQL("SELECT 1 FROM profiles WHERE userid = $::FORM{'user'}"); diff --git a/t/002goodperl.t b/t/002goodperl.t index 9c99a799a..09a5f0324 100644 --- a/t/002goodperl.t +++ b/t/002goodperl.t @@ -55,13 +55,40 @@ foreach my $file (@testitems) { } my $file_line1 = ; close (FILE); + + $file =~ m/.*\.(.*)/; + my $ext = $1; + if ($file_line1 !~ /\/usr\/bonsaitools\/bin\/perl/) { ok(1,"$file does not have a shebang"); } else { - if ($file_line1 =~ m#/usr/bonsaitools/bin/perl -w#) { - ok(1,"$file uses -w"); + my $flags; + if ($file eq "processmail") { + # special case processmail, which is tainted checked + $flags = "wT"; + } elsif (!defined $ext || $ext eq "pl") { + # standalone programs (eg syncshadowdb) aren't taint checked yet + $flags = "w"; + } elsif ($ext eq "pm") { + ok(0, "$file is a module, but has a shebang"); + next; + } elsif ($ext eq "cgi") { + # cgi files must be taint checked, but only the user-accessible + # ones have been checked so far + if ($file =~ m/^edit/) { + $flags = "w"; + } else { + $flags = "wT"; + } + } else { + ok(0, "$file has shebang but unknown extension"); + next; + } + + if ($file_line1 =~ m#/usr/bonsaitools/bin/perl -$flags#) { + ok(1,"$file uses -$flags"); } else { - ok(0,"$file is MISSING -w --WARNING"); + ok(0,"$file is MISSING -$flags --WARNING"); } } } diff --git a/token.cgi b/token.cgi index 2fed15ad0..81ae29629 100755 --- a/token.cgi +++ b/token.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -28,6 +28,8 @@ use diagnostics; use strict; +use lib qw(.); + # Include the Bugzilla CGI and general utility library. require "CGI.pl"; diff --git a/userprefs.cgi b/userprefs.cgi index bc0f1d672..531d57c0e 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -22,6 +22,8 @@ use diagnostics; use strict; +use lib qw(.); + require "CGI.pl"; use RelationSet; diff --git a/xml.cgi b/xml.cgi index 51093890e..8b71b3837 100755 --- a/xml.cgi +++ b/xml.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bonsaitools/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public @@ -23,10 +23,13 @@ use diagnostics; use strict; + +use lib qw(.); + use Bug; require "CGI.pl"; -if (!defined $::FORM{'id'} || $::FORM{'id'} !~ /^\s*\d+(,\d+)*\s*$/) { +if (!defined $::FORM{'id'} || !$::FORM{'id'}) { print "Content-type: text/html\n\n"; PutHeader("Display as XML"); print "
\n"; -- cgit v1.2.3-65-gdbad From f15fc6e6632c6a360b5f620cf929084a07a4c403 Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Tue, 5 Feb 2002 06:16:18 +0000 Subject: bug 122418 - setting attachment status fails taint checks. Just needed to detaint after checking. r=gerv,kiko --- attachment.cgi | 2 ++ 1 file changed, 2 insertions(+) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 5996aa86d..368ad702e 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -257,6 +257,8 @@ sub validateStatuses || DisplayError("One of the statuses you entered is not a valid status for this attachment.") && exit; + # We have tested that the status is valid, so it can be detainted + detaint_natural($status); } } -- cgit v1.2.3-65-gdbad From 7fa045d01faf68f35814861d4c13ab8a7127c0d4 Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Tue, 5 Feb 2002 08:11:48 +0000 Subject: Bug 122418 pt2 - obsoleting a patch from the create attachment screen gave a taint error. --- attachment.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 368ad702e..f58844835 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -314,7 +314,7 @@ sub validateObsolete # Make sure the attachment id is valid and the user has permissions to view # the bug to which it is attached. foreach my $attachid (@{$::MFORM{'obsolete'}}) { - $attachid =~ /^[1-9][0-9]*$/ + detaint_natural($attachid) || DisplayError("The attachment number of one of the attachments you wanted to obsolete is invalid.") && exit; -- cgit v1.2.3-65-gdbad From e1235d08fc52ff4cb914efecda9b15c2b7943f5a Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Wed, 13 Feb 2002 10:27:20 +0000 Subject: Bug 100094 - use generic template handling code r=mattyt, afranke --- Attachment.pm | 35 +++++++-------------- Bugzilla/Attachment.pm | 35 +++++++-------------- attachment.cgi | 34 +++------------------ colchange.cgi | 42 ++++---------------------- createaccount.cgi | 26 +++------------- editattachstatuses.cgi | 34 +++------------------ enter_bug.cgi | 4 --- globals.pl | 17 +++++++++-- index.cgi | 35 +++------------------ query.cgi | 82 ++++++++++++-------------------------------------- quips.cgi | 26 ++++------------ sidebar.cgi | 36 +++------------------- 12 files changed, 93 insertions(+), 313 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 4b4b19ec3..1b6d74062 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -27,25 +27,15 @@ use diagnostics; use strict; +use vars qw( + $template + $vars +); + package Attachment; -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "templates/" subdirectory. -use Template; - -# This is the global template object that gets used one or more times by -# the script when it needs to process a template and return the results. -# Configuration parameters can be specified here that apply to all templates -# processed in this file. -my $template = Template->new( - { - # Colon-separated list of directories containing templates. - INCLUDE_PATH => 'template/custom:template/default' , - # Allow templates to be specified with relative paths. - RELATIVE => 1 - } -); +my $template = $::template; +my $vars = $::vars; # This module requires that its caller have said "require CGI.pl" to import # relevant functions from that script and its companion globals.pl. @@ -99,16 +89,13 @@ sub list push @attachments, \%a; } - my $vars = - { - 'bugid' => $bugid , - 'attachments' => \@attachments , - 'Param' => \&::Param , # for retrieving global parameters - 'PerformSubsts' => \&::PerformSubsts # for processing global parameters - }; + $vars->{'bugid'} = $bugid; + $vars->{'attachments'} = \@attachments; $template->process("attachment/list.atml", $vars) || &::DisplayError("Template process failed: " . $template->error()) && exit; } + +1; diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 4b4b19ec3..1b6d74062 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -27,25 +27,15 @@ use diagnostics; use strict; +use vars qw( + $template + $vars +); + package Attachment; -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "templates/" subdirectory. -use Template; - -# This is the global template object that gets used one or more times by -# the script when it needs to process a template and return the results. -# Configuration parameters can be specified here that apply to all templates -# processed in this file. -my $template = Template->new( - { - # Colon-separated list of directories containing templates. - INCLUDE_PATH => 'template/custom:template/default' , - # Allow templates to be specified with relative paths. - RELATIVE => 1 - } -); +my $template = $::template; +my $vars = $::vars; # This module requires that its caller have said "require CGI.pl" to import # relevant functions from that script and its companion globals.pl. @@ -99,16 +89,13 @@ sub list push @attachments, \%a; } - my $vars = - { - 'bugid' => $bugid , - 'attachments' => \@attachments , - 'Param' => \&::Param , # for retrieving global parameters - 'PerformSubsts' => \&::PerformSubsts # for processing global parameters - }; + $vars->{'bugid'} = $bugid; + $vars->{'attachments'} = \@attachments; $template->process("attachment/list.atml", $vars) || &::DisplayError("Template process failed: " . $template->error()) && exit; } + +1; diff --git a/attachment.cgi b/attachment.cgi index f58844835..ea17c29e5 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -31,41 +31,17 @@ use strict; use lib qw(.); +use vars qw( + $template + $vars +); + # Include the Bugzilla CGI and general utility library. require "CGI.pl"; # Establish a connection to the database backend. ConnectToDatabase(); -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "template/" subdirectory. -use Template; - -# Create the global template object that processes templates and specify -# configuration parameters that apply to all templates processed in this script. -my $template = Template->new( - { - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default" , - # Allow templates to be specified with relative paths. - RELATIVE => 1 - } -); - -# Define the global variables and functions that will be passed to the UI -# template. Individual functions add their own values to this hash before -# sending them to the templates they process. -my $vars = - { - # Function for retrieving global parameters. - 'Param' => \&Param , - - # Function for processing global parameters that contain references - # to other global parameters. - 'PerformSubsts' => \&PerformSubsts - }; - # Check whether or not the user is logged in and, if so, set the $::userid # and $::usergroupset variables. quietly_check_login(); diff --git a/colchange.cgi b/colchange.cgi index 5d3222afc..f2ac55aa9 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -25,45 +25,15 @@ use strict; use lib qw(.); -sub sillyness { # shut up "used only once" warnings - my $zz = @::legal_keywords; - $zz = $::buffer; -} +use vars qw( + @legal_keywords + $buffer + $template + $vars +); require "CGI.pl"; -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "template/" subdirectory. -use Template; - -# Create the global template object that processes templates and specify -# configuration parameters that apply to all templates processed in this script. -my $template = Template->new( -{ - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default", - # Allow templates to be specified with relative paths. - RELATIVE => 1, - PRE_CHOMP => 1, -}); - -# Define the global variables and functions that will be passed to the UI -# template. Individual functions add their own values to this hash before -# sending them to the templates they process. -my $vars = -{ - # Function for retrieving global parameters. - 'Param' => \&Param, - - # Function for processing global parameters that contain references - # to other global parameters. - 'PerformSubsts' => \&PerformSubsts, - - # Function to search an array for a value - 'lsearch' => \&lsearch, -}; - print "Content-type: text/html\n"; # The master list not only says what fields are possible, but what order diff --git a/createaccount.cgi b/createaccount.cgi index 86a994483..ecf3a68f6 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -32,27 +32,11 @@ use lib qw(.); require "CGI.pl"; # Shut up misguided -w warnings about "used only once": -use vars %::FORM; - -# Use the template toolkit (http://www.template-toolkit.org/) -use Template; - -# Create the global template object that processes templates -my $template = Template->new( -{ - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default", - RELATIVE => 1, - PRE_CHOMP => 1, -}); - -# Define the global variables and functions that will be passed to the UI -# template. -my $vars = -{ - 'Param' => \&Param, - 'PerformSubsts' => \&PerformSubsts, -}; +use vars qw( + %FORM + $template + $vars +); ConnectToDatabase(); diff --git a/editattachstatuses.cgi b/editattachstatuses.cgi index 6596a79c9..0642d6bb8 100755 --- a/editattachstatuses.cgi +++ b/editattachstatuses.cgi @@ -29,41 +29,17 @@ use diagnostics; use strict; +use vars qw( + $template + $vars +); + # Include the Bugzilla CGI and general utility library. require "CGI.pl"; # Establish a connection to the database backend. ConnectToDatabase(); -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "template/" subdirectory. -use Template; - -# Create the global template object that processes templates and specify -# configuration parameters that apply to all templates processed in this script. -my $template = Template->new( - { - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default" , - # Allow templates to be specified with relative paths. - RELATIVE => 1 - } -); - -# Define the global variables and functions that will be passed to the UI -# template. Individual functions add their own values to this hash before -# sending them to the templates they process. -my $vars = - { - # Function for retrieving global parameters. - 'Param' => \&Param , - - # Function for processing global parameters that contain references - # to other global parameters. - 'PerformSubsts' => \&PerformSubsts - }; - # Make sure the user is logged in and is allowed to edit products # (i.e. the user has "editcomponents" privileges), since attachment # statuses are product-specific. diff --git a/enter_bug.cgi b/enter_bug.cgi index eea2947bd..0ab18d9a9 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -51,10 +51,6 @@ use vars qw( %MFORM ); -# Use template variables -my $template = $::template; -my $vars = $::vars; - # If we're using bug groups to restrict bug entry, we need to know who the # user is right from the start. confirm_login() if (Param("usebuggroupsentry")); diff --git a/globals.pl b/globals.pl index f88347f13..e1f3302f3 100644 --- a/globals.pl +++ b/globals.pl @@ -1578,9 +1578,6 @@ $::template = Template->new( # Colon-separated list of directories containing templates. INCLUDE_PATH => "template/custom:template/default" , - # Allow templates to be specified with relative paths. - RELATIVE => 1 , - # Remove white-space before template directives (PRE_CHOMP) and at the # beginning and end of templates and template blocks (TRIM) for better # looking, more compact content. Use the plus sign at the beginning @@ -1593,6 +1590,17 @@ $::template = Template->new( { # Render text in strike-through style. strike => sub { return "" . $_[0] . "" } , + + # Returns the text with backslashes, single/double quotes, + # and newlines/carriage returns escaped for use in JS strings. + js => sub + { + my ($var) = @_; + $var =~ s/([\\\'\"])/\\$1/g; + $var =~ s/\n/\\n/g; + $var =~ s/\r/\\r/g; + return $var; + } } , } ); @@ -1652,6 +1660,9 @@ $::vars = # Function for processing global parameters that contain references # to other global parameters. 'PerformSubsts' => \&PerformSubsts , + + # Generic linear search function + 'lsearch' => \&lsearch , }; 1; diff --git a/index.cgi b/index.cgi index 5c300992d..2fe393391 100755 --- a/index.cgi +++ b/index.cgi @@ -37,38 +37,13 @@ use strict; use lib "."; require "CGI.pl"; -# Establish a connection to the database backend. -ConnectToDatabase(); - -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "template/" subdirectory. -use Template; - -# Create the global template object that processes templates and specify -# configuration parameters that apply to all templates processed in this script. -my $template = Template->new( - { - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default", - # Allow templates to be specified with relative paths. - RELATIVE => 1, - POST_CHOMP => 1, - } +use vars qw( + $template + $vars ); -# Define the global variables and functions that will be passed to the UI -# template. Individual functions add their own values to this hash before -# sending them to the templates they process. -my $vars = - { - # Function for retrieving global parameters. - 'Param' => \&Param , - - # Function for processing global parameters that contain references - # to other global parameters. - 'PerformSubsts' => \&PerformSubsts - }; +# Establish a connection to the database backend. +ConnectToDatabase(); # Check whether or not the user is logged in and, if so, set the $::userid # and $::usergroupset variables. diff --git a/query.cgi b/query.cgi index 3149d07b7..b41620d36 100755 --- a/query.cgi +++ b/query.cgi @@ -34,68 +34,26 @@ require "CGI.pl"; # that no longer exist), since we don't want to die in the query page. $::CheckOptionValues = 0; -use vars - @::CheckOptionValues, - @::legal_resolution, - @::legal_bug_status, - @::legal_components, - @::legal_keywords, - @::legal_opsys, - @::legal_platform, - @::legal_priority, - @::legal_product, - @::legal_severity, - @::legal_target_milestone, - @::legal_versions, - @::log_columns, - %::versions, - %::components, - %::FORM; - -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "template/" subdirectory. -use Template; - -# Create the global template object that processes templates and specify -# configuration parameters that apply to all templates processed in this script. -my $template = Template->new( -{ - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default", - # Allow templates to be specified with relative paths. - RELATIVE => 1, - PRE_CHOMP => 1, - FILTERS => - { - # Returns the text with backslashes, single/double quotes, - # and newlines/carriage returns escaped for use in JS strings. - 'js' => sub - { - my ($var) = @_; - $var =~ s/([\\\'\"])/\\$1/g; - $var =~ s/\n/\\n/g; - $var =~ s/\r/\\r/g; - return $var; - } - }, -}); - -# Define the global variables and functions that will be passed to the UI -# template. Individual functions add their own values to this hash before -# sending them to the templates they process. -my $vars = -{ - # Function for retrieving global parameters. - 'Param' => \&Param, - - # Function for processing global parameters that contain references - # to other global parameters. - 'PerformSubsts' => \&PerformSubsts, - - # Function to search an array for a value - 'lsearch' => \&lsearch, -}; +use vars qw( + @CheckOptionValues + @legal_resolution + @legal_bug_status + @legal_components + @legal_keywords + @legal_opsys + @legal_platform + @legal_priority + @legal_product + @legal_severity + @legal_target_milestone + @legal_versions + @log_columns + %versions + %components + %FORM + $template + $vars +); if (defined $::FORM{"GoAheadAndLogIn"}) { # We got here from a login page, probably from relogin.cgi. We better diff --git a/quips.cgi b/quips.cgi index f1a41b4b9..1b5213e97 100755 --- a/quips.cgi +++ b/quips.cgi @@ -23,31 +23,17 @@ use diagnostics; use strict; -use vars ( %::FORM ); + +use vars qw( + %FORM + $template + $vars +); use lib qw(.); require "CGI.pl"; -# Use the template toolkit (http://www.template-toolkit.org/) -use Template; - -# Create the global template object that processes templates -my $template = Template->new( -{ - INCLUDE_PATH => "template/custom:template/default", - RELATIVE => 1, - PRE_CHOMP => 1, -}); - -# Define the global variables and functions that will be passed to the UI -# template. -my $vars = -{ - 'Param' => \&Param, - 'PerformSubsts' => \&PerformSubsts, -}; - my $action = $::FORM{'action'} || ""; if ($action eq "show") { diff --git a/sidebar.cgi b/sidebar.cgi index 073a1ecff..385086b5b 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -22,39 +22,13 @@ use lib "."; require "CGI.pl"; # Shut up "Used Only Once" errors -use vars qw { $anyvotesallowed }; - -ConnectToDatabase(); - -# Use the template toolkit (http://www.template-toolkit.org/) to generate -# the user interface (HTML pages and mail messages) using templates in the -# "template/" subdirectory. -use Template; - -# Create the global template object that processes templates and specify -# configuration parameters that apply to all templates processed in this script. -my $template = Template->new( - { - # Colon-separated list of directories containing templates. - INCLUDE_PATH => "template/custom:template/default", - # Allow templates to be specified with relative paths. - RELATIVE => 1, - POST_CHOMP =>1, - } +use vars qw( + $anyvotesallowed + $template + $vars ); -# Define the global variables and functions that will be passed to the UI -# template. Individual functions add their own values to this hash before -# sending them to the templates they process. -my $vars = - { - # Function for retrieving global parameters. - 'Param' => \&Param , - - # Function that tells us if the logged in user is in a specific group. - 'UserInGroup' => \&UserInGroup , - }; - +ConnectToDatabase(); # Needed for $::anyvotesallowed GetVersionTable(); -- cgit v1.2.3-65-gdbad From 8e03a8495526725e59ab07586afbb4130ad5f7cd Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Tue, 26 Feb 2002 14:13:33 +0000 Subject: Bug 97729 - uploaders need to be able to obsolete their own attachments r=jake, justdave --- Attachment.pm | 14 ++++++-- Bugzilla/Attachment.pm | 14 ++++++-- attachment.cgi | 60 +++++++++++++++++++++++------------ template/default/attachment/list.atml | 6 +++- 4 files changed, 68 insertions(+), 26 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 1b6d74062..7416fd589 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -51,17 +51,21 @@ sub list my ($bugid) = @_; + my $in_editbugs = &::UserInGroup("editbugs"); # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. &::SendSQL(" - SELECT attach_id, creation_ts, mimetype, description, ispatch, isobsolete + SELECT attach_id, creation_ts, mimetype, description, ispatch, + isobsolete, submitter_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); while (&::MoreSQLData()) { my %a; - ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = &::FetchSQLData(); + my $submitter_id; + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, + $a{'ispatch'}, $a{'isobsolete'}, $submitter_id) = &::FetchSQLData(); # Format the attachment's creation/modification date into a standard # format (YYYY-MM-DD HH:MM) @@ -86,6 +90,12 @@ sub list $a{'statuses'} = \@statuses; &::PopGlobalSQLState(); + # We will display the edit link if the user can edit the attachment; + # ie the are the submitter, or they have canedit. + # Also show the link if the user is not logged in - in that cae, + # They'll be prompted later + $a{'canedit'} = ($::userid == 0 || $submitter_id == $::userid || + $in_editbugs); push @attachments, \%a; } diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 1b6d74062..7416fd589 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -51,17 +51,21 @@ sub list my ($bugid) = @_; + my $in_editbugs = &::UserInGroup("editbugs"); # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. &::SendSQL(" - SELECT attach_id, creation_ts, mimetype, description, ispatch, isobsolete + SELECT attach_id, creation_ts, mimetype, description, ispatch, + isobsolete, submitter_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); while (&::MoreSQLData()) { my %a; - ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = &::FetchSQLData(); + my $submitter_id; + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, + $a{'ispatch'}, $a{'isobsolete'}, $submitter_id) = &::FetchSQLData(); # Format the attachment's creation/modification date into a standard # format (YYYY-MM-DD HH:MM) @@ -86,6 +90,12 @@ sub list $a{'statuses'} = \@statuses; &::PopGlobalSQLState(); + # We will display the edit link if the user can edit the attachment; + # ie the are the submitter, or they have canedit. + # Also show the link if the user is not logged in - in that cae, + # They'll be prompted later + $a{'canedit'} = ($::userid == 0 || $submitter_id == $::userid || + $in_editbugs); push @attachments, \%a; } diff --git a/attachment.cgi b/attachment.cgi index ea17c29e5..32b4ef461 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -87,16 +87,16 @@ elsif ($action eq "insert") } elsif ($action eq "edit") { + quietly_check_login(); validateID(); + validateCanEdit($::FORM{'id'}); edit(); } elsif ($action eq "update") { confirm_login(); - UserInGroup("editbugs") - || DisplayError("You are not authorized to edit attachments.") - && exit; validateID(); + validateCanEdit($::FORM{'id'}); validateDescription(); validateIsPatch(); validateContentType() unless $::FORM{'ispatch'}; @@ -135,6 +135,28 @@ sub validateID ValidateBugID($bugid); } +sub validateCanEdit +{ + my ($attach_id) = (@_); + + # If the user is not logged in, claim that they can edit. This allows + # the edit scrren to be displayed to people who aren't logged in. + # People not logged in can't actually commit changes, because that code + # calls confirm_login, not quietly_check_login, before calling this sub + return if $::userid == 0; + + # People in editbugs can edit all attachments + return if UserInGroup("editbugs"); + + # Bug 97729 - the submitter can edit their attachments + SendSQL("SELECT attach_id FROM attachments WHERE " . + "attach_id = $attach_id AND submitter_id = $::userid"); + + FetchSQLData() + || DisplayError("You are not authorised to edit attachment #$attach_id") + && exit; +} + sub validateDescription { $::FORM{'description'} @@ -278,15 +300,6 @@ sub validateFilename sub validateObsolete { - # When a user creates an attachment, they can request that one or more - # existing attachments be made obsolete. This function makes sure they - # are authorized to make changes to attachments and that the IDs of the - # attachments they selected for obsoletion are all valid. - UserInGroup("editbugs") - || DisplayError("You must be authorized to make changes to attachments - to make attachments obsolete when creating a new attachment.") - && exit; - # Make sure the attachment id is valid and the user has permissions to view # the bug to which it is attached. foreach my $attachid (@{$::MFORM{'obsolete'}}) { @@ -305,9 +318,6 @@ sub validateObsolete my ($bugid, $isobsolete, $description) = FetchSQLData(); - # Make sure the user is authorized to access this attachment's bug. - ValidateBugID($bugid); - if ($bugid != $::FORM{'bugid'}) { $description = html_quote($description); @@ -323,6 +333,9 @@ sub validateObsolete DisplayError("Attachment #$attachid ($description) is already obsolete."); exit; } + + # Check that the user can modify this attachment + validateCanEdit($attachid); } } @@ -411,12 +424,16 @@ sub enter { # Display a form for entering a new attachment. - # Retrieve the attachments from the database and write them into an array - # of hashes where each hash represents one attachment. + # Retrieve the attachments the user can edit from the database and write + # them into an array of hashes where each hash represents one attachment. + my $canEdit = ""; + if (!UserInGroup("editbugs")) { + $canEdit = "AND submitter_id = $::userid"; + } SendSQL("SELECT attach_id, description FROM attachments WHERE bug_id = $::FORM{'bugid'} - AND isobsolete = 0 + AND isobsolete = 0 $canEdit ORDER BY attach_id"); my @attachments; # the attachments array while ( MoreSQLData() ) { @@ -516,9 +533,10 @@ sub insert sub edit { - # Edit an attachment record. Users with "editbugs" privileges can edit the - # attachment's description, content type, ispatch and isobsolete flags, and - # statuses, and they can also submit a comment that appears in the bug. + # Edit an attachment record. Users with "editbugs" privileges, (or the + # original attachment's submitter) can edit the attachment's description, + # content type, ispatch and isobsolete flags, and statuses, and they can + # also submit a comment that appears in the bug. # Users cannot edit the content of the attachment itself. # Retrieve the attachment from the database. diff --git a/template/default/attachment/list.atml b/template/default/attachment/list.atml index 90306a65b..f8fe4c96d 100755 --- a/template/default/attachment/list.atml +++ b/template/default/attachment/list.atml @@ -61,7 +61,11 @@ - Edit + [% IF attachment.canedit %] + Edit + [% ELSE %] + None + [% END %] [% END %] -- cgit v1.2.3-65-gdbad From 0930c074fa7d8dabed00af2ba39a177a8d00167f Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Mon, 11 Mar 2002 15:33:03 +0000 Subject: Fix for bug 72184: prevents users from entering too-large comments/descriptions that get rejected by MySQL's MAX_PACKET_SIZE restrictions. Patch by Myk Melez . r=bbaetz,gerv --- CGI.pl | 13 +++++++++++++ attachment.cgi | 2 ++ post_bug.cgi | 2 ++ process_bug.cgi | 2 ++ 4 files changed, 19 insertions(+) (limited to 'attachment.cgi') diff --git a/CGI.pl b/CGI.pl index f91cbd670..0882a967c 100644 --- a/CGI.pl +++ b/CGI.pl @@ -324,6 +324,19 @@ sub ValidateBugID { } + +sub ValidateComment { + # Make sure a comment is not too large (greater than 64K). + + my ($comment) = @_; + + if (defined($comment) && length($comment) > 65535) { + DisplayError("Comments cannot be longer than 65,535 characters."); + exit; + } +} + + # check and see if a given string actually represents a positive # integer, and abort if not. # diff --git a/attachment.cgi b/attachment.cgi index 32b4ef461..66c3236a7 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -77,6 +77,7 @@ elsif ($action eq "insert") { confirm_login(); ValidateBugID($::FORM{'bugid'}); + ValidateComment($::FORM{'comment'}); validateFilename(); validateData(); validateDescription(); @@ -95,6 +96,7 @@ elsif ($action eq "edit") elsif ($action eq "update") { confirm_login(); + ValidateComment($::FORM{'comment'}); validateID(); validateCanEdit($::FORM{'id'}); validateDescription(); diff --git a/post_bug.cgi b/post_bug.cgi index 0ae44d32f..58048ef7e 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -49,6 +49,8 @@ sub sillyness { confirm_login(); +ValidateComment($::FORM{'comment'}); + my $cookiepath = Param("cookiepath"); print "Set-Cookie: PLATFORM=$::FORM{'product'} ; path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n" if ( exists $::FORM{'product'} ); print "Set-Cookie: VERSION-$::FORM{'product'}=$::FORM{'version'} ; path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n" if ( exists $::FORM{'product'} && exists $::FORM{'version'} ); diff --git a/process_bug.cgi b/process_bug.cgi index f2abb0390..fb3c0e482 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -92,6 +92,8 @@ if (defined $::FORM{'dup_id'} && $::FORM{'knob'} eq "duplicate") { DuplicateUserConfirm(); } +ValidateComment($::FORM{'comment'}); + ###################################################################### # End Data/Security Validation ###################################################################### -- cgit v1.2.3-65-gdbad From 05d101e58b400b1a52adcc86515b5442b85cd2f5 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Wed, 24 Apr 2002 14:24:43 +0000 Subject: Bug 138588 - change to use new template structure. Patch by gerv, r=myk, afranke. --- Bugzilla/Token.pm | 15 ++++++--------- CGI.pl | 2 +- Token.pm | 15 ++++++--------- attachment.cgi | 26 ++++++++++---------------- bug_form.pl | 9 ++++----- buglist.cgi | 22 ++++++++++------------ colchange.cgi | 5 ++--- createaccount.cgi | 13 ++++++------- describecomponents.cgi | 8 ++++---- describekeywords.cgi | 5 ++--- duplicates.cgi | 5 ++--- editattachstatuses.cgi | 20 ++++++++------------ enter_bug.cgi | 8 ++++---- index.cgi | 5 ++--- long_list.cgi | 5 ++--- post_bug.cgi | 11 ++++++----- process_bug.cgi | 20 ++++++++++---------- query.cgi | 5 ++--- quips.cgi | 5 ++--- relogin.cgi | 3 +-- show_activity.cgi | 5 ++--- showdependencygraph.cgi | 5 ++--- showdependencytree.cgi | 5 ++--- sidebar.cgi | 5 ++--- token.cgi | 25 +++++++++---------------- userprefs.cgi | 5 ++--- votes.cgi | 14 ++++++-------- xml.cgi | 7 +++---- 28 files changed, 118 insertions(+), 160 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 9c136184b..39584bd9c 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -71,9 +71,8 @@ sub IssueEmailChangeToken { $vars->{'emailaddress'} = $old_email . &::Param('emailsuffix'); my $message; - $template->process("token/emailchangeold.txt.tmpl", $vars, \$message) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/email/change-old.txt.tmpl", $vars, \$message) + || &::ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -83,9 +82,8 @@ sub IssueEmailChangeToken { $vars->{'emailaddress'} = $new_email . &::Param('emailsuffix'); $message = ""; - $template->process("token/emailchangenew.txt.tmpl", $vars, \$message) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/email/change-new.txt.tmpl", $vars, \$message) + || &::ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -222,9 +220,8 @@ sub Cancel { # Notify the user via email about the cancellation. my $message; - $template->process("token/tokencancel.txt.tmpl", $vars, \$message) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/cancel-token.txt.tmpl", $vars, \$message) + || &::ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; diff --git a/CGI.pl b/CGI.pl index b7a513f2a..983513306 100644 --- a/CGI.pl +++ b/CGI.pl @@ -69,7 +69,7 @@ if (Param("shutdownhtml") && $0 !~ m:[\\/](do)?editparams.cgi$:) { # Generate and return an HTML message about the downtime. $::template->process("global/message.html.tmpl", $::vars) - || DisplayError("Template process failed: " . $::template->error()); + || ThrowTemplateError($::template->error()); exit; } diff --git a/Token.pm b/Token.pm index 9c136184b..39584bd9c 100644 --- a/Token.pm +++ b/Token.pm @@ -71,9 +71,8 @@ sub IssueEmailChangeToken { $vars->{'emailaddress'} = $old_email . &::Param('emailsuffix'); my $message; - $template->process("token/emailchangeold.txt.tmpl", $vars, \$message) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/email/change-old.txt.tmpl", $vars, \$message) + || &::ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -83,9 +82,8 @@ sub IssueEmailChangeToken { $vars->{'emailaddress'} = $new_email . &::Param('emailsuffix'); $message = ""; - $template->process("token/emailchangenew.txt.tmpl", $vars, \$message) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/email/change-new.txt.tmpl", $vars, \$message) + || &::ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -222,9 +220,8 @@ sub Cancel { # Notify the user via email about the cancellation. my $message; - $template->process("token/tokencancel.txt.tmpl", $vars, \$message) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/cancel-token.txt.tmpl", $vars, \$message) + || &::ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; diff --git a/attachment.cgi b/attachment.cgi index 66c3236a7..f45b4efd3 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -416,9 +416,8 @@ sub viewall print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/viewall.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("attachment/show-multiple.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -459,9 +458,8 @@ sub enter print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/enter.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("attachment/create.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -527,9 +525,8 @@ sub insert print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/created.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("attachment/created.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -603,9 +600,8 @@ sub edit print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/edit.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("attachment/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -787,8 +783,6 @@ sub update print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/updated.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; - + $template->process("attachment/updated.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } diff --git a/bug_form.pl b/bug_form.pl index 792e816ad..b074cdb15 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -67,8 +67,8 @@ sub show_bug { my $id = $::FORM{'id'}; if (!defined($id)) { - $template->process("show/choose_bug.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + $template->process("bug/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); exit; } @@ -312,9 +312,8 @@ sub show_bug { $vars->{'user'} = \%user; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("show/show_bug.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("bug/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } 1; diff --git a/buglist.cgi b/buglist.cgi index 53685e96d..31f15b02e 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -69,7 +69,7 @@ ConnectToDatabase(); # Determine the format in which the user would like to receive the output. # Uses the default format if the user did not specify an output format; # otherwise validates the user's choice against the list of available formats. -my $format = ValidateOutputFormat($::FORM{'format'}); +my $format = ValidateOutputFormat($::FORM{'format'}, "list"); # Whether or not the user wants to change multiple bugs. my $dotweak = $::FORM{'tweak'} ? 1 : 0; @@ -117,7 +117,7 @@ if ($::buffer =~ /&cmd-/) { $vars->{'url'} = $url; $vars->{'link'} = "Click here if the page does not redisplay automatically."; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + || ThrowTemplateError($template->error()); exit; } @@ -1053,7 +1053,7 @@ CMD: for ($::FORM{'cmdtype'}) { $vars->{'url'} = $url; $vars->{'link'} = "Click here if the page does not redisplay automatically."; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + || ThrowTemplateError($template->error()); exit; }; @@ -1069,7 +1069,7 @@ CMD: for ($::FORM{'cmdtype'}) { $vars->{'url'} = "query.cgi"; $vars->{'link'} = "Go back to the query page."; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + || ThrowTemplateError($template->error()); exit; }; @@ -1088,7 +1088,7 @@ CMD: for ($::FORM{'cmdtype'}) { $vars->{'url'} = "query.cgi"; $vars->{'link'} = "Go back to the query page, using the new default."; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + || ThrowTemplateError($template->error()); exit; }; @@ -1128,7 +1128,7 @@ CMD: for ($::FORM{'cmdtype'}) { $vars->{'url'} = "query.cgi"; $vars->{'link'} = "Go back to the query page."; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + || ThrowTemplateError($template->error()); exit; }; } @@ -1361,9 +1361,8 @@ if ($serverpush) { print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("buglist/server-push.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Connect to the shadow database if this installation is using one to improve @@ -1565,9 +1564,8 @@ print "\n"; # end HTTP headers ################################################################################ # Generate and return the UI (HTML page) from the appropriate template. -$template->process("buglist/$format->{'template'}", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("list/$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); ################################################################################ diff --git a/colchange.cgi b/colchange.cgi index f2ac55aa9..ea21042e2 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -119,7 +119,6 @@ $vars->{buffer} = $::buffer; # Generate and return the UI (HTML page) from the appropriate template. print "Content-type: text/html\n\n"; -$template->process("buglist/colchange.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("list/change-columns.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/createaccount.cgi b/createaccount.cgi index 9f87f4612..2456b9343 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -68,8 +68,8 @@ if (defined($login)) { if (!ValidateNewUser($login)) { # Account already exists - $template->process("admin/account_exists.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + $template->process("account/exists.html.tmpl", $vars) + || ThrowTemplateError($template->error()); exit; } @@ -77,12 +77,11 @@ if (defined($login)) { my $password = InsertNewUser($login, $realname); MailPassword($login, $password); - $template->process("admin/account_created.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + $template->process("account/created.html.tmpl", $vars) + || DisplayError($template->error()); exit; } # Show the standard "would you like to create an account?" form. -$template->process("admin/create_account.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("account/create.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/describecomponents.cgi b/describecomponents.cgi index 57eef27cf..8d2c219f4 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -67,8 +67,8 @@ if (!defined $::FORM{'product'}) { "Please specify the product whose components you want described."; print "Content-type: text/html\n\n"; - $::template->process("global/choose_product.tmpl", $::vars) - || DisplayError("Template process failed: " . $::template->error()); + $::template->process("global/choose-product.html.tmpl", $::vars) + || ThrowTemplateError($::template->error()); exit; } @@ -123,6 +123,6 @@ $::vars->{'product'} = $product; $::vars->{'components'} = \@components; print "Content-type: text/html\n\n"; -$::template->process("info/describe-components.tmpl", $::vars) - || DisplayError("Template process failed: " . $::template->error()); +$::template->process("reports/components.html.tmpl", $::vars) + || ThrowTemplateError($::template->error()); diff --git a/describekeywords.cgi b/describekeywords.cgi index d588100c6..503e0d5a3 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -54,6 +54,5 @@ $vars->{'keywords'} = \@keywords; $vars->{'caneditkeywords'} = UserInGroup("editkeywords"); print "Content-type: text/html\n\n"; -$template->process("info/describe-keywords.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("reports/keywords.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/duplicates.cgi b/duplicates.cgi index 11eb52f49..98e7e0a22 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -197,9 +197,8 @@ $vars->{'products'} = \@::legal_product; print "Content-type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. -$template->process("report/duplicates.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("reports/duplicates.html.tmpl", $vars) + || ThrowTemplateError($template->error()); sub days_ago { diff --git a/editattachstatuses.cgi b/editattachstatuses.cgi index 61e1a15ae..f4176a321 100755 --- a/editattachstatuses.cgi +++ b/editattachstatuses.cgi @@ -201,9 +201,8 @@ sub list print "Content-type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachstatus/list.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("admin/attachstatus/list.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -223,9 +222,8 @@ sub create print "Content-type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachstatus/create.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("admin/attachstatus/create.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -272,9 +270,8 @@ sub edit print "Content-type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachstatus/edit.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("admin/attachstatus/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } @@ -322,9 +319,8 @@ sub confirmDelete print "Content-type: text/html\n\n"; - $template->process("attachstatus/delete.atml", $vars) - || DisplayError("Template process failed: " . & $template->error()) - && exit; + $template->process("admin/attachstatus/delete.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } else { deleteStatus(); diff --git a/enter_bug.cgi b/enter_bug.cgi index ec008b9d6..dce3dd4ac 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -86,8 +86,8 @@ if (!defined $::FORM{'product'}) { "First, you must pick a product on which to enter a bug."; print "Content-type: text/html\n\n"; - $template->process("global/choose_product.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + $template->process("global/choose-product.html.tmpl", $vars) + || ThrowTemplateError($template->error()); exit; } @@ -355,6 +355,6 @@ if ($::usergroupset ne '0') { $vars->{'default'} = \%default; print "Content-type: text/html\n\n"; -$template->process("entry/enter_bug.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); +$template->process("bug/create/create.html.tmpl", $vars) + || ThrowTemplateError($template->error()); exit; diff --git a/index.cgi b/index.cgi index 2fe393391..05f15d288 100755 --- a/index.cgi +++ b/index.cgi @@ -60,6 +60,5 @@ $vars->{'subst'} = { 'userid' => $vars->{'username'} }; print "Content-Type: text/html\n\n"; # Generate and return the UI (HTML page) from the appropriate template. -$template->process("index.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("index.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/long_list.cgi b/long_list.cgi index b25c10ebf..a828b8711 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -112,6 +112,5 @@ print "Content-Type: text/html\n"; print "Content-Disposition: inline; filename=$filename\n\n"; # Generate and return the UI (HTML page) from the appropriate template. -$template->process("show/multiple.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("bug/show-multiple.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/post_bug.cgi b/post_bug.cgi index a50369592..b727396d0 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -57,7 +57,8 @@ confirm_login(); my $comment; $vars->{'form'} = \%::FORM; -$template->process("entry/comment.txt.tmpl", $vars, \$comment) + +$template->process("bug/create/initial-comment.txt.tmpl", $vars, \$comment) || ThrowTemplateError($template->error()); ValidateComment($comment); @@ -80,8 +81,8 @@ if (defined $::FORM{'maketemplate'}) { $vars->{'url'} = $::buffer; print "Content-type: text/html\n\n"; - $template->process("entry/create-template.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + $template->process("bug/create/make-template.html.tmpl", $vars) + || ThrowTemplateError($template->error()); exit; } @@ -301,5 +302,5 @@ $vars->{'bug_id'} = $id; $vars->{'mailresults'} = $mailresults; print "Content-type: text/html\n\n"; -$template->process("entry/post-bug.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); +$template->process("bug/create/created.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/process_bug.cgi b/process_bug.cgi index e89fea970..35c24e24e 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -108,7 +108,7 @@ print "Content-type: text/html\n\n"; # Start displaying the response page. $vars->{'title'} = "Bug processed"; -$template->process("global/header", $vars) +$template->process("global/header.html.tmpl", $vars) || ThrowTemplateError($template->error()); $vars->{'header_done'} = 1; @@ -210,7 +210,7 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct) $vars->{'verify_bug_group'} = (Param('usebuggroups') && !defined($::FORM{'addtonewgroup'})); - $template->process("process/verify-new-product.html.tmpl", $vars) + $template->process("bug/process/verify-new-product.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } @@ -332,7 +332,7 @@ sub DuplicateUserConfirm { # Confirm whether or not to add the reporter to the cc: list # of the original bug (the one this bug is being duped against). print "Content-type: text/html\n\n"; - $template->process("process/confirm-dupe.html.tmpl", $vars) + $template->process("bug/process/confirm-duplicate.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # end DuplicateUserConfirm() @@ -873,7 +873,7 @@ foreach my $id (@idlist) { SendSQL("UNLOCK TABLES"); # Warn the user about the mid-air collision and ask them what to do. - $template->process("process/mid-air.html.tmpl", $vars) + $template->process("bug/process/midair.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } @@ -1251,7 +1251,7 @@ foreach my $id (@idlist) { # Let the user know the bug was changed and who did and didn't # receive email about the change. - $template->process("process/results.html.tmpl", $vars) + $template->process("bug/process/results.html.tmpl", $vars) || ThrowTemplateError($template->error()); if ($duplicate) { @@ -1281,7 +1281,7 @@ foreach my $id (@idlist) { $vars->{'type'} = "dupe"; # Let the user know a duplication notation was added to the original bug. - $template->process("process/results.html.tmpl", $vars) + $template->process("bug/process/results.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -1297,7 +1297,7 @@ foreach my $id (@idlist) { # Let the user know we checked to see if we should email notice # of this change to users with a relationship to the dependent # bug and who did and didn't receive email about it. - $template->process("process/results.html.tmpl", $vars) + $template->process("bug/process/results.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -1316,7 +1316,7 @@ if ($::COOKIE{"BUGLIST"} && $::FORM{'id'}) { $vars->{'next_id'} = $next_bug; # Let the user know we are about to display the next bug in their list. - $template->process("process/next-bug.html.tmpl", $vars) + $template->process("bug/process/next.html.tmpl", $vars) || ThrowTemplateError($template->error()); show_bug("header is already done"); @@ -1327,7 +1327,7 @@ if ($::COOKIE{"BUGLIST"} && $::FORM{'id'}) { } # End the response page. -$template->process("show/navigate.html.tmpl", $vars) +$template->process("bug/navigate.html.tmpl", $vars) || ThrowTemplateError($template->error()); -$template->process("global/footer", $vars) +$template->process("global/footer.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/query.cgi b/query.cgi index fe6fe1622..c2d5a3ba6 100755 --- a/query.cgi +++ b/query.cgi @@ -381,6 +381,5 @@ $vars->{'default'} = \%default; # Generate and return the UI (HTML page) from the appropriate template. print "Content-type: text/html\n\n"; -$template->process("query/query.atml", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("search/search.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/quips.cgi b/quips.cgi index 1b5213e97..e685fb207 100755 --- a/quips.cgi +++ b/quips.cgi @@ -70,6 +70,5 @@ if ($action eq "add") { } print "Content-type: text/html\n\n"; -$template->process("info/quips.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("list/quips.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/relogin.cgi b/relogin.cgi index 03670158d..d07de9ef6 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -69,8 +69,7 @@ delete $::COOKIE{"Bugzilla_login"}; print "Content-Type: text/html\n\n"; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + || ThrowTemplateError($template->error()); exit; diff --git a/show_activity.cgi b/show_activity.cgi index ca0e52fa2..59e43b672 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -57,7 +57,6 @@ $vars->{'bug_id'} = $::FORM{'id'}; print "Content-type: text/html\n\n"; -$template->process("show/bug-activity.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("bug/activity/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index f30e4d24f..21725b8f9 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -190,6 +190,5 @@ $vars->{'showsummary'} = $::FORM{'showsummary'}; # Generate and return the UI (HTML page) from the appropriate template. print "Content-type: text/html\n\n"; -$template->process("show/dependency-graph.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("bug/dependency-graph.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/showdependencytree.cgi b/showdependencytree.cgi index 0288faaa1..7f5d12d5c 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -94,9 +94,8 @@ $vars->{'hide_resolved'} = $hide_resolved; $vars->{'canedit'} = UserInGroup("editbugs"); print "Content-Type: text/html\n\n"; -$template->process("show/dependency-tree.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("bug/dependency-tree.html.tmpl", $vars) + || ThrowTemplateError($template->error()); ################################################################################ # Recursive Tree Generation Function # diff --git a/sidebar.cgi b/sidebar.cgi index 385086b5b..2f1b92a69 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -73,9 +73,8 @@ my $useragent = $ENV{HTTP_USER_AGENT}; if ($useragent =~ m:Mozilla/([1-9][0-9]*):i && $1 >= 5 && $useragent !~ m/compatible/i) { print "Content-type: application/vnd.mozilla.xul+xml\n\n"; # Generate and return the XUL from the appropriate template. - $template->process("sidebar/xul.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("sidebar.xul.tmpl", $vars) + || ThrowTemplateError($template->error()); } else { DisplayError("sidebar.cgi currently only supports Mozilla based web browsers"); exit; diff --git a/token.cgi b/token.cgi index e8fb3f90f..e1ae0b35b 100755 --- a/token.cgi +++ b/token.cgi @@ -178,8 +178,7 @@ sub requestChangePassword { print "Content-Type: text/html\n\n"; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + || ThrowTemplateError($template->error()); } sub confirmChangePassword { @@ -187,9 +186,8 @@ sub confirmChangePassword { $vars->{'token'} = $::token; print "Content-Type: text/html\n\n"; - $template->process("admin/change-password.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/password/set-forgotten-password.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } sub cancelChangePassword { @@ -200,8 +198,7 @@ sub cancelChangePassword { print "Content-Type: text/html\n\n"; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + || ThrowTemplateError($template->error()); } sub changePassword { @@ -229,8 +226,7 @@ sub changePassword { print "Content-Type: text/html\n\n"; $template->process("global/message.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + || ThrowTemplateError($template->error()); } sub confirmChangeEmail { @@ -240,9 +236,8 @@ sub confirmChangeEmail { $vars->{'title'} = "Confirm Change Email"; $vars->{'token'} = $::token; - $template->process("token/confirmemail.html.tmpl", $vars) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("account/email/confirm.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } sub changeEmail { @@ -287,8 +282,7 @@ sub changeEmail { $vars->{'message'} = "Your Bugzilla login has been changed."; $template->process("global/message.html.tmpl", $vars) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + || ThrowTemplateError($template->error()); } sub cancelChangeEmail { @@ -336,7 +330,6 @@ sub cancelChangeEmail { $vars->{'title'} = "Cancel Request to Change Email Address"; $template->process("global/message.html.tmpl", $vars) - || &::DisplayError("Template process failed: " . $template->error()) - && exit; + || ThrowTemplateError($template->error()); } diff --git a/userprefs.cgi b/userprefs.cgi index ddeb0f1f7..79690563c 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -409,7 +409,6 @@ SWITCH: for ($current_tab_name) { # Generate and return the UI (HTML page) from the appropriate template. print "Content-type: text/html\n\n"; -$template->process("prefs/userprefs.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; +$template->process("account/prefs/prefs.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/votes.cgi b/votes.cgi index a6a86f426..eee96b960 100755 --- a/votes.cgi +++ b/votes.cgi @@ -110,9 +110,8 @@ sub show_bug { $vars->{'total'} = $total; print "Content-type: text/html\n\n"; - $template->process("voting/show-bug-votes.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("bug/votes/list-for-bug.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Display all the votes for a particular user. If it's the user @@ -218,9 +217,8 @@ sub show_user { $vars->{'products'} = \@products; print "Content-type: text/html\n\n"; - $template->process("voting/show-user-votes.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; + $template->process("bug/votes/list-for-user.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Update the user's votes in the database. @@ -239,8 +237,8 @@ sub record_votes { if (scalar(@buglist) == 0) { if (!defined($::FORM{'delete_all_votes'})) { print "Content-type: text/html\n\n"; - $template->process("voting/delete-all-votes.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()); + $template->process("bug/votes/delete-all.html.tmpl", $vars) + || ThrowTemplateError($template->error()); exit(); } elsif ($::FORM{'delete_all_votes'} == 0) { diff --git a/xml.cgi b/xml.cgi index 1773ec6cf..8356c3d89 100755 --- a/xml.cgi +++ b/xml.cgi @@ -34,10 +34,9 @@ use vars qw($template $vars); if (!defined $::FORM{'id'} || !$::FORM{'id'}) { print "Content-Type: text/html\n\n"; - $template->process("show/choose_xml.html.tmpl", $vars) - || DisplayError("Template process failed: " . $template->error()) - && exit; - exit; + $template->process("bug/choose-xml.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; } quietly_check_login(); -- cgit v1.2.3-65-gdbad From 4bc6d3653390bc268789c91803b1953accff520a Mon Sep 17 00:00:00 2001 From: "jouni%heikniemi.net" <> Date: Thu, 4 Jul 2002 16:12:33 +0000 Subject: Bug 62000: File attachments don't work on Windows. Note: only the code from the patch was checked in, the documentation issue was split to bug 155743. 2xr=bbaetz --- attachment.cgi | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index f45b4efd3..a755d3b26 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -36,6 +36,12 @@ use vars qw( $vars ); +# Win32 specific hack to avoid a hang when creating/showing an attachment +if ($^O eq 'MSWin32') { + binmode(STDIN); + binmode(STDOUT); +} + # Include the Bugzilla CGI and general utility library. require "CGI.pl"; -- cgit v1.2.3-65-gdbad From 61ddf0a32846fdf2607043d94af1a0a86b80f6fc Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Mon, 12 Aug 2002 12:42:42 +0000 Subject: Bug 43600 - Convert products/components to use ids instead of names. Initial attempt by jake@bugzilla.org, updated by me r=joel, preed --- Bug.pm | 9 ++- Bugzilla/Bug.pm | 9 ++- Bugzilla/Search.pm | 58 +++++++++++++++--- CGI.pl | 2 +- attachment.cgi | 4 +- bug_form.pl | 14 ++++- buglist.cgi | 6 +- checksetup.pl | 155 ++++++++++++++++++++++++++++++++++++++++--------- collectstats.pl | 7 ++- contrib/bug_email.pl | 36 +++++++----- describecomponents.cgi | 13 +++-- duplicates.cgi | 24 +++++--- editattachstatuses.cgi | 32 +++++----- editcomponents.cgi | 114 ++++++++++++++++-------------------- editgroups.cgi | 4 +- editmilestones.cgi | 54 +++++++++-------- editproducts.cgi | 121 +++++++++++++++++++------------------- editusers.cgi | 14 +++-- editversions.cgi | 73 ++++++++++------------- enter_bug.cgi | 10 ++-- globals.pl | 62 ++++++++++++++++++-- importxml.pl | 7 ++- long_list.cgi | 9 +-- post_bug.cgi | 27 ++++++--- process_bug.cgi | 68 ++++++++++++++++++---- queryhelp.cgi | 8 ++- reports.cgi | 14 +++-- sanitycheck.cgi | 52 ++++++++--------- votes.cgi | 11 ++-- 29 files changed, 651 insertions(+), 366 deletions(-) (limited to 'attachment.cgi') diff --git a/Bug.pm b/Bug.pm index df7a91553..d73bc536f 100755 --- a/Bug.pm +++ b/Bug.pm @@ -111,13 +111,16 @@ sub initBug { my $query = " select - bugs.bug_id, alias, product, version, rep_platform, op_sys, bug_status, - resolution, priority, bug_severity, component, assigned_to, reporter, + 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'), groupset, delta_ts, sum(votes.count) - from bugs left join votes using(bug_id) + from bugs left join votes using(bug_id), + products, components where bugs.bug_id = $bug_id + AND products.id = bugs.product_id + AND components.id = bugs.component_id group by bugs.bug_id"; &::SendSQL(&::SelectVisible($query, $user_id, $usergroupset)); diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index df7a91553..d73bc536f 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -111,13 +111,16 @@ sub initBug { my $query = " select - bugs.bug_id, alias, product, version, rep_platform, op_sys, bug_status, - resolution, priority, bug_severity, component, assigned_to, reporter, + 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'), groupset, delta_ts, sum(votes.count) - from bugs left join votes using(bug_id) + from bugs left join votes using(bug_id), + products, components where bugs.bug_id = $bug_id + AND products.id = bugs.product_id + AND components.id = bugs.component_id group by bugs.bug_id"; &::SendSQL(&::SelectVisible($query, $user_id, $usergroupset)); diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 0f311f07e..16dd493d6 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -66,13 +66,29 @@ sub init { my @andlist; # First, deal with all the old hard-coded non-chart-based poop. - unshift(@supptables, - ("profiles map_assigned_to", - "profiles map_reporter", - "LEFT JOIN profiles map_qa_contact ON bugs.qa_contact = map_qa_contact.userid")); - unshift(@wherepart, - ("bugs.assigned_to = map_assigned_to.userid", - "bugs.reporter = map_reporter.userid")); + if (&::lsearch($fieldsref, 'map_assigned_to.login_name') >= 0) { + push @supptables, "profiles AS map_assigned_to"; + push @wherepart, "bugs.assigned_to = map_assigned_to.userid"; + } + + if (&::lsearch($fieldsref, 'map_reporter.login_name') >= 0) { + push @supptables, "profiles AS map_reporter"; + push @wherepart, "bugs.assigned_to = map_reporter.userid"; + } + + if (&::lsearch($fieldsref, 'map_qa_contact.login_name') >= 0) { + push @supptables, "LEFT JOIN profiles map_qa_contact ON bugs.qa_contact = map_qa_contact.userid"; + } + + if (&::lsearch($fieldsref, 'map_products.name') >= 0) { + push @supptables, "products AS map_products"; + push @wherepart, "bugs.product_id = map_products.id"; + } + + if (&::lsearch($fieldsref, 'map_components.name') >= 0) { + push @supptables, "components AS map_components"; + push @wherepart, "bugs.component_id = map_components.id"; + } my $minvotes; if (defined $F{'votes'}) { @@ -108,6 +124,18 @@ sub init { } } + if ($F{'product'}) { + push(@supptables, "products products_"); + push(@wherepart, "products_.id = bugs.product_id"); + push(@specialchart, ["products_.name", "anyexact", $F{'product'}]); + } + + if ($F{'component'}) { + push(@supptables, "components components_"); + push(@wherepart, "components_.id = bugs.component_id"); + push(@specialchart, ["components_.name", "anyexact", $F{'component'}]); + } + if ($F{'keywords'}) { my $t = $F{'keywords_type'}; if (!$t || $t eq "or") { @@ -248,7 +276,7 @@ sub init { my @funcdefs = ( "^(assigned_to|reporter)," => sub { - push(@supptables, "profiles map_$f"); + push(@supptables, "profiles AS map_$f"); push(@wherepart, "bugs.$f = map_$f.userid"); $f = "map_$f.login_name"; }, @@ -391,6 +419,20 @@ sub init { $f = "(to_days(now()) - to_days(bugs.delta_ts))"; }, + "^component,(?!changed)" => sub { + my $table = "components_$chartid"; + push(@supptables, "components $table"); + push(@wherepart, "bugs.component_id = $table.id"); + $f = $ff = "$table.name"; + }, + + "^product,(?!changed)" => sub { + my $table = "products_$chartid"; + push(@supptables, "products $table"); + push(@wherepart, "bugs.product_id = $table.id"); + $f = $ff = "$table.name"; + }, + "^keywords," => sub { &::GetVersionTable(); my @list; diff --git a/CGI.pl b/CGI.pl index d3ca9f8f9..de2d38085 100644 --- a/CGI.pl +++ b/CGI.pl @@ -947,7 +947,7 @@ sub CheckIfVotedConfirmed { SendSQL("SELECT bugs.votes, bugs.bug_status, products.votestoconfirm, " . " bugs.everconfirmed " . "FROM bugs, products " . - "WHERE bugs.bug_id = $id AND products.product = bugs.product"); + "WHERE bugs.bug_id = $id AND products.product_id = bugs.product_id"); my ($votes, $status, $votestoconfirm, $everconfirmed) = (FetchSQLData()); if ($votes >= $votestoconfirm && $status eq $::unconfirmedstate) { SendSQL("UPDATE bugs SET bug_status = 'NEW', everconfirmed = 1 " . diff --git a/attachment.cgi b/attachment.cgi index a755d3b26..8a6c1b033 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -252,7 +252,7 @@ sub validateStatuses FROM attachments, bugs, attachstatusdefs WHERE attachments.attach_id = $::FORM{'id'} AND attachments.bug_id = bugs.bug_id - AND attachstatusdefs.product = bugs.product"); + AND attachstatusdefs.product_id = bugs.product_id"); my @statusdefs; push(@statusdefs, FetchSQLData()) while MoreSQLData(); PopGlobalSQLState(); @@ -573,7 +573,7 @@ sub edit SendSQL("SELECT id, name FROM attachstatusdefs, bugs WHERE bug_id = $bugid - AND attachstatusdefs.product = bugs.product + AND attachstatusdefs.product_id = bugs.product_id ORDER BY sortkey"); while ( MoreSQLData() ) { diff --git a/bug_form.pl b/bug_form.pl index 65c7b41c1..fb6a60112 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -77,19 +77,27 @@ sub show_bug { # Populate the bug hash with the info we get directly from the DB. my $query = " - SELECT bugs.bug_id, alias, product, version, rep_platform, + SELECT bugs.bug_id, alias, products.name, version, rep_platform, op_sys, bug_status, resolution, priority, - bug_severity, component, assigned_to, reporter, + 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'), groupset, delta_ts, sum(votes.count) - FROM bugs LEFT JOIN votes USING(bug_id) + 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()) { + ThrowCodeError("No data when fetching bug $id"); + } + my $value; my @row = FetchSQLData(); foreach my $field ("bug_id", "alias", "product", "version", "rep_platform", diff --git a/buglist.cgi b/buglist.cgi index 589719962..9570cdab3 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -397,8 +397,8 @@ DefineColumn("resolution" , "bugs.resolution" , "Result" DefineColumn("summary" , "bugs.short_desc" , "Summary" ); DefineColumn("summaryfull" , "bugs.short_desc" , "Summary" ); DefineColumn("status_whiteboard" , "bugs.status_whiteboard" , "Status Summary" ); -DefineColumn("component" , "bugs.component" , "Component" ); -DefineColumn("product" , "bugs.product" , "Product" ); +DefineColumn("component" , "map_components.name" , "Component" ); +DefineColumn("product" , "map_products.name" , "Product" ); DefineColumn("version" , "bugs.version" , "Version" ); DefineColumn("os" , "bugs.op_sys" , "OS" ); DefineColumn("target_milestone" , "bugs.target_milestone" , "Target Milestone" ); @@ -561,7 +561,7 @@ if ($order) { # change it to order by the sortkey of the target_milestone first. if ($db_order =~ /bugs.target_milestone/) { $db_order =~ s/bugs.target_milestone/ms_order.sortkey,ms_order.value/; - $query =~ s/\sWHERE\s/ LEFT JOIN milestones ms_order ON ms_order.value = bugs.target_milestone AND ms_order.product = bugs.product WHERE /; + $query =~ s/\sWHERE\s/ LEFT JOIN milestones ms_order ON ms_order.value = bugs.target_milestone AND ms_order.product_id = bugs.product_id WHERE /; } # If we are sorting by votes, sort in descending order if no explicit diff --git a/checksetup.pl b/checksetup.pl index 74d008708..919fb0354 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -23,6 +23,8 @@ # Dan Mosedale # Dave Miller # Zach Lipton +# Jacob Steenhagen +# Bradley Baetz # # # Direct any questions on this source code to @@ -1324,7 +1326,7 @@ $table{attachstatusdefs} = name VARCHAR(50) NOT NULL , description MEDIUMTEXT NULL , sortkey SMALLINT NOT NULL DEFAULT 0 , - product VARCHAR(64) NOT NULL + product_id SMALLINT NOT NULL '; # @@ -1343,11 +1345,11 @@ $table{bugs} = short_desc mediumtext, op_sys enum($my_opsys) not null, priority enum($my_priorities) not null, - product varchar(64) not null, + product_id smallint not null, rep_platform enum($my_platforms), reporter mediumint not null, version varchar(64) not null, - component varchar(50) not null, + component_id smallint not null, resolution enum("", "FIXED", "INVALID", "WONTFIX", "LATER", "REMIND", "DUPLICATE", "WORKSFORME", "MOVED") not null, target_milestone varchar(20) not null default "---", qa_contact mediumint not null, @@ -1369,10 +1371,10 @@ $table{bugs} = index (bug_status), index (op_sys), index (priority), - index (product), + index (product_id), index (reporter), index (version), - index (component), + index (component_id), index (resolution), index (target_milestone), index (qa_contact), @@ -1408,11 +1410,15 @@ $table{longdescs} = $table{components} = - 'value tinytext, - program varchar(64), + 'id smallint not null auto_increment primary key, + name varchar(64) not null, + product_id smallint not null, initialowner mediumint not null, initialqacontact mediumint not null, - description mediumtext not null'; + description mediumtext not null, + + unique(product_id,name), + index(name)'; $table{dependencies} = @@ -1462,15 +1468,17 @@ $table{logincookies} = $table{products} = - 'product varchar(64), + 'id smallint not null auto_increment primary key, + name varchar(64) not null, description mediumtext, milestoneurl tinytext not null, disallownew tinyint not null, votesperuser smallint not null, maxvotesperbug smallint not null default 10000, votestoconfirm smallint not null, - defaultmilestone varchar(20) not null default "---" -'; + defaultmilestone varchar(20) not null default "---", + + unique(name)'; $table{profiles} = @@ -1521,7 +1529,7 @@ $table{fielddefs} = $table{versions} = 'value tinytext, - program varchar(64) not null'; + product_id smallint not null'; $table{votes} = @@ -1548,10 +1556,10 @@ $table{keyworddefs} = $table{milestones} = - 'value varchar(20) not null, - product varchar(64) not null, + 'product_id smallint not null, + value varchar(20) not null, sortkey smallint not null, - unique (product, value)'; + unique (product_id, value)'; $table{shadowlog} = 'id int not null auto_increment primary key, @@ -2077,22 +2085,27 @@ _End_Of_SQL_ $sth->execute; my ($adminuid) = $sth->fetchrow_array; if (!$adminuid) { die "No administator!" } # should never get here -$sth = $dbh->prepare("SELECT product FROM products"); +$sth = $dbh->prepare("SELECT description FROM products"); $sth->execute; unless ($sth->rows) { print "Creating initial dummy product 'TestProduct' ...\n"; - $dbh->do('INSERT INTO products(product, description, milestoneurl, disallownew, votesperuser, votestoconfirm) VALUES ("TestProduct", + $dbh->do('INSERT INTO products(name, description, milestoneurl, disallownew, votesperuser, votestoconfirm) VALUES ("TestProduct", "This is a test product. This ought to be blown away and ' . 'replaced with real stuff in a finished installation of ' . 'bugzilla.", "", 0, 0, 0)'); - $dbh->do('INSERT INTO versions (value, program) VALUES ("other", "TestProduct")'); - $dbh->do("INSERT INTO components (value, program, description, initialowner, initialqacontact) + # We could probably just assume that this is "1", but better + # safe than sorry... + $sth = $dbh->prepare("SELECT LAST_INSERT_ID()"); + $sth->execute; + my ($product_id) = $sth->fetchrow_array; + $dbh->do(qq{INSERT INTO versions (value, product_id) VALUES ("other", $product_id)}); + $dbh->do("INSERT INTO components (name, product_id, description, initialowner, initialqacontact) VALUES (" . - "'TestComponent', 'TestProduct', " . + "'TestComponent', $product_id, " . "'This is a test component in the test product database. " . "This ought to be blown away and replaced with real stuff in " . "a finished installation of bugzilla.', $adminuid, 0)"); - $dbh->do('INSERT INTO milestones (product, value) VALUES ("TestProduct","---")'); + $dbh->do(qq{INSERT INTO milestones (product_id, value) VALUES ($product_id,"---")}); } @@ -2206,8 +2219,6 @@ AddField('products', 'disallownew', 'tinyint not null'); AddField('products', 'milestoneurl', 'tinytext not null'); AddField('components', 'initialqacontact', 'tinytext not null'); AddField('components', 'description', 'mediumtext not null'); -ChangeFieldType('components', 'program', 'varchar(64)'); - # 1999-06-22 Added an entry to the attachments table to record who the # submitter was. Nothing uses this yet, but it still should be recorded. @@ -2258,10 +2269,14 @@ AddField('products', 'votesperuser', 'mediumint not null'); # tinytext is equivalent to varchar(255), which is quite huge, so I change # them all to varchar(64). -ChangeFieldType ('bugs', 'product', 'varchar(64) not null'); -ChangeFieldType ('components', 'program', 'varchar(64)'); -ChangeFieldType ('products', 'product', 'varchar(64)'); -ChangeFieldType ('versions', 'program', 'varchar(64) not null'); +# Only do this if these fields still exist - they're removed below, in +# a later change +if (GetFieldDef('products', 'product')) { + ChangeFieldType ('bugs', 'product', 'varchar(64) not null'); + ChangeFieldType ('components', 'program', 'varchar(64)'); + ChangeFieldType ('products', 'product', 'varchar(64)'); + ChangeFieldType ('versions', 'program', 'varchar(64) not null'); +} # 2000-01-16 Added a "keywords" field to the bugs table, which # contains a string copy of the entries of the keywords table for this @@ -3045,6 +3060,92 @@ if (-e 'data/comments.bak' && !$renamed_comments_file) { if (GetFieldDef("namedqueries", "watchfordiffs")) { DropField("namedqueries", "watchfordiffs"); } + +# 2002-08-?? jake@acutex.net/bbaetz@student.usyd.edu.au - bug 43600 +# Use integer IDs for products and components. +if (GetFieldDef("products", "product")) { + print "Updating database to use product IDs.\n"; + + # First, we need to remove possible NULL entries + # NULLs may exist, but won't have been used, since all the uses of them + # are in NOT NULL fields in other tables + $dbh->do("DELETE FROM products WHERE product IS NULL"); + $dbh->do("DELETE FROM components WHERE value IS NULL"); + + AddField("products", "id", "smallint not null auto_increment primary key"); + AddField("components", "product_id", "smallint not null"); + AddField("versions", "product_id", "smallint not null"); + AddField("milestones", "product_id", "smallint not null"); + AddField("bugs", "product_id", "smallint not null"); + AddField("attachstatusdefs", "product_id", "smallint not null"); + my %products; + my $sth = $dbh->prepare("SELECT id, product FROM products"); + $sth->execute; + while (my ($product_id, $product) = $sth->fetchrow_array()) { + if (exists $products{$product}) { + print "Ignoring duplicate product $product\n"; + $dbh->do("DELETE FROM products WHERE id = $product_id"); + next; + } + $products{$product} = 1; + $dbh->do("UPDATE components SET product_id = $product_id " . + "WHERE program = " . $dbh->quote($product)); + $dbh->do("UPDATE versions SET product_id = $product_id " . + "WHERE program = " . $dbh->quote($product)); + $dbh->do("UPDATE milestones SET product_id = $product_id " . + "WHERE product = " . $dbh->quote($product)); + $dbh->do("UPDATE bugs SET product_id = $product_id, delta_ts=delta_ts " . + "WHERE product = " . $dbh->quote($product)); + $dbh->do("UPDATE attachstatusdefs SET product_id = $product_id " . + "WHERE product = " . $dbh->quote($product)); + } + + print "Updating the database to use component IDs.\n"; + AddField("components", "id", "smallint not null auto_increment primary key"); + AddField("bugs", "component_id", "smallint not null"); + my %components; + $sth = $dbh->prepare("SELECT id, value, product_id FROM components"); + $sth->execute; + while (my ($component_id, $component, $product_id) = $sth->fetchrow_array()) { + if (exists $components{$component}) { + if (exists $components{$component}{$product_id}) { + print "Ignoring duplicate component $component for product $product_id\n"; + $dbh->do("DELETE FROM components WHERE id = $component_id"); + next; + } + } else { + $components{$component} = {}; + } + $components{$component}{$product_id} = 1; + $dbh->do("UPDATE bugs SET component_id = $component_id, delta_ts=delta_ts " . + "WHERE component = " . $dbh->quote($component) . + " AND product_id = $product_id"); + } + print "Fixing Indexes and Uniqueness.\n"; + $dbh->do("ALTER TABLE milestones DROP INDEX product"); + $dbh->do("ALTER TABLE milestones ADD UNIQUE (product_id, value)"); + $dbh->do("ALTER TABLE bugs DROP INDEX product"); + $dbh->do("ALTER TABLE bugs ADD INDEX (product_id)"); + $dbh->do("ALTER TABLE bugs DROP INDEX component"); + $dbh->do("ALTER TABLE bugs ADD INDEX (component_id)"); + + print "Removing, renaming, and retyping old product and component fields.\n"; + DropField("components", "program"); + DropField("versions", "program"); + DropField("milestones", "product"); + DropField("bugs", "product"); + DropField("bugs", "component"); + DropField("attachstatusdefs", "product"); + RenameField("products", "product", "name"); + ChangeFieldType("products", "name", "varchar(64) not null"); + RenameField("components", "value", "name"); + ChangeFieldType("components", "name", "varchar(64) not null"); + + print "Adding indexes for products and components tables.\n"; + $dbh->do("ALTER TABLE products ADD UNIQUE (name)"); + $dbh->do("ALTER TABLE components ADD UNIQUE (product_id, name)"); + $dbh->do("ALTER TABLE components ADD INDEX (name)"); +} # If you had to change the --TABLE-- definition in any way, then add your # differential change code *** A B O V E *** this comment. diff --git a/collectstats.pl b/collectstats.pl index 4e69ab9b5..a47e2174d 100755 --- a/collectstats.pl +++ b/collectstats.pl @@ -67,6 +67,9 @@ sub collect_stats { my $dir = shift; my $product = shift; my $when = localtime (time); + my $product_id = get_product_id($product) unless $product eq '-All-'; + + die "Unknown product $product" unless ($product_id or $product eq '-All-'); # NB: Need to mangle the product for the filename, but use the real # product name in the query @@ -82,7 +85,7 @@ sub collect_stats { if( $product eq "-All-" ) { SendSQL("select count(bug_status) from bugs where bug_status='$status'"); } else { - SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'"); + SendSQL("select count(bug_status) from bugs where bug_status='$status' and product_id=$product_id"); } push @row, FetchOneColumn(); @@ -92,7 +95,7 @@ sub collect_stats { if( $product eq "-All-" ) { SendSQL("select count(resolution) from bugs where resolution='$resolution'"); } else { - SendSQL("select count(resolution) from bugs where resolution='$resolution' and product='$product'"); + SendSQL("select count(resolution) from bugs where resolution='$resolution' and product_id=$product_id"); } push @row, FetchOneColumn(); diff --git a/contrib/bug_email.pl b/contrib/bug_email.pl index cb103bbfb..7817a0f80 100755 --- a/contrib/bug_email.pl +++ b/contrib/bug_email.pl @@ -37,7 +37,7 @@ # # You need to work with bug_email.pl the MIME::Parser installed. # -# $Id: bug_email.pl,v 1.10 2002/07/25 01:47:19 justdave%syndicomm.com Exp $ +# $Id: bug_email.pl,v 1.11 2002/08/12 05:43:05 bbaetz%student.usyd.edu.au Exp $ ############################################################### # 02/12/2000 (SML) @@ -196,7 +196,7 @@ sub CheckPermissions { sub CheckProduct { my $Product = shift; - SendSQL("select product from products where product='$Product'"); + SendSQL("select name from products where name = " . SqlQuote($Product)); my $Result = FetchOneColumn(); if (lc($Result) eq lc($Product)) { return $Result; @@ -211,7 +211,7 @@ sub CheckComponent { my $Product = shift; my $Component = shift; - SendSQL("select value from components where program=" . SqlQuote($Product) . " and value=" . SqlQuote($Component) . ""); + SendSQL("select components.name from components, products where components.product_id = products.id AND products.name=" . SqlQuote($Product) . " and components.name=" . SqlQuote($Component)); my $Result = FetchOneColumn(); if (lc($Result) eq lc($Component)) { return $Result; @@ -226,7 +226,7 @@ sub CheckVersion { my $Product = shift; my $Version = shift; - SendSQL("select value from versions where program=" . SqlQuote($Product) . " and value=" . SqlQuote($Version) . ""); + SendSQL("select value from versions, products where versions.product_id = products.id AND products.name=" . SqlQuote($Product) . " and value=" . SqlQuote($Version)); my $Result = FetchOneColumn(); if (lc($Result) eq lc($Version)) { return $Result; @@ -840,9 +840,9 @@ if (! CheckPermissions("CreateBugs", $SenderShort ) ) { # Set QA if (Param("useqacontact")) { - SendSQL("select initialqacontact from components where program=" . + SendSQL("select initialqacontact from components, products where components.product_id = products.id AND products.name=" . SqlQuote($Control{'product'}) . - " and value=" . SqlQuote($Control{'component'})); + " and components.name=" . SqlQuote($Control{'component'})); $Control{'qacontact'} = FetchOneColumn(); } @@ -863,7 +863,7 @@ if ( $Product eq "" ) { $Text .= "Valid products are:\n\t"; - SendSQL("select product from products"); + SendSQL("select name from products ORDER BY name"); @all_products = FetchAllSQLData(); $Text .= join( "\n\t", @all_products ) . "\n\n"; $Text .= horLine(); @@ -903,7 +903,7 @@ if ( $Component eq "" ) { foreach my $prod ( @all_products ) { $Text .= "\nValid components for product `$prod' are: \n\t"; - SendSQL("select value from components where program=" . SqlQuote( $prod ) . ""); + SendSQL("SELECT components.name FROM components, products WHERE components.product_id=products.id AND products.name = " . SqlQuote($prod)); @val_components = FetchAllSQLData(); $Text .= join( "\n\t", @val_components ) . "\n"; @@ -936,9 +936,10 @@ if ( defined($Control{'assigned_to'}) && $Control{'assigned_to'} !~ /^\s*$/ ) { $Control{'assigned_to'} = DBname_to_id($Control{'assigned_to'}); } else { - SendSQL("select initialowner from components where program=" . + SendSQL("select initialowner from components, products where " . + " components.product_id=products.id AND products.name=" . SqlQuote($Control{'product'}) . - " and value=" . SqlQuote($Control{'component'})); + " and components.name=" . SqlQuote($Control{'component'})); $Control{'assigned_to'} = FetchOneColumn(); } @@ -982,7 +983,7 @@ if ( $Version eq "" ) { foreach my $prod ( @all_products ) { $Text .= "Valid versions for product " . SqlQuote( $prod ) . " are: \n\t"; - SendSQL("select value from versions where program=" . SqlQuote( $prod ) . ""); + SendSQL("select value from versions, products where versions.product_id=products.id AND products.name=" . SqlQuote( $prod )); @all_versions = FetchAllSQLData(); $anz_versions = @all_versions; $Text .= join( "\n\t", @all_versions ) . "\n" ; @@ -1176,11 +1177,20 @@ END my $query = "insert into bugs (\n" . join(",\n", @used_fields ) . ", bug_status, creation_ts, everconfirmed) values ( "; + # 'Yuck'. Then again, this whole file should be rewritten anyway... + $query =~ s/product/product_id/; + $query =~ s/component/component_id/; + my $tmp_reply = "These values were stored by bugzilla:\n"; my $val; foreach my $field (@used_fields) { if( $field eq "groupset" ) { $query .= $Control{$field} . ",\n"; + } elsif ( $field eq 'product' ) { + $query .= get_product_id($Control{$field}) . ",\n"; + } elsif ( $field eq 'component' ) { + $query .= get_component_id(get_product_id($Control{'product'}), + $Control{$field}) . ",\n"; } else { $query .= SqlQuote($Control{$field}) . ",\n"; } @@ -1210,8 +1220,8 @@ END my $ever_confirmed = 0; my $state = SqlQuote("UNCONFIRMED"); - SendSQL("SELECT votestoconfirm FROM products WHERE product = " . - SqlQuote($Control{'product'}) . ";"); + SendSQL("SELECT votestoconfirm FROM products WHERE name = " . + SqlQuote($Control{'product'})); if (!FetchOneColumn()) { $ever_confirmed = 1; $state = SqlQuote("NEW"); diff --git a/describecomponents.cgi b/describecomponents.cgi index 2f723757e..edf9349ab 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -23,6 +23,7 @@ use vars qw( %FORM + %legal_product $userid ); @@ -85,9 +86,9 @@ my $product = $::FORM{'product'}; # which could enable people guessing product names to determine # whether or not certain products exist in Bugzilla, even if they # cannot get any other information about that product. -grep($product eq $_ , @::legal_product) - || DisplayError("The product name is invalid.") - && exit; +my $product_id = get_product_id($product); + +ThrowUserError("The product name is invalid.") unless $product_id; # Make sure the user is authorized to access this product. if (Param("usebuggroups") && GroupExists($product)) { @@ -102,9 +103,9 @@ if (Param("usebuggroups") && GroupExists($product)) { ###################################################################### my @components; -SendSQL("SELECT value, initialowner, initialqacontact, description FROM " . - "components WHERE program = " . SqlQuote($product) . " ORDER BY " . - "value"); +SendSQL("SELECT name, initialowner, initialqacontact, description FROM " . + "components WHERE product_id = $product_id ORDER BY " . + "name"); while (MoreSQLData()) { my ($name, $initialowner, $initialqacontact, $description) = FetchSQLData(); diff --git a/duplicates.cgi b/duplicates.cgi index 3d0875313..715b4be28 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -62,6 +62,15 @@ my $product = formvalue("product"); my $sortvisible = formvalue("sortvisible"); my @buglist = (split(/[:,]/, formvalue("bug_id"))); +my $product_id; +if ($product) { + $product_id = get_product_id($product); + if (!$product_id) { + ThrowUserError("The product " . html_quote($product) . + " does not exist"); + } +} + # Small backwards-compatibility hack, dated 2002-04-10. $sortby = "count" if $sortby eq "dup_count"; @@ -143,16 +152,17 @@ if (scalar(%count)) { # WONTFIX. We want to see VERIFIED INVALID and WONTFIX because common # "bugs" which aren't bugs end up in this state. my $query = " - SELECT bugs.bug_id, component, bug_severity, op_sys, target_milestone, - short_desc, bug_status, resolution - FROM bugs - WHERE (bug_status != 'CLOSED') - AND ((bug_status = 'VERIFIED' AND resolution IN ('INVALID', 'WONTFIX')) + SELECT bugs.bug_id, components.name, bug_severity, op_sys, + target_milestone, short_desc, bug_status, resolution + FROM bugs, components + WHERE (bugs.component_id = components.id) + AND (bug_status != 'CLOSED') + AND ((bug_status = 'VERIFIED' AND resolution IN ('INVALID', 'WONTFIX')) OR (bug_status != 'VERIFIED')) AND bugs.bug_id IN (" . join(", ", keys %count) . ")"; - # Limit to a single product if requested - $query .= (" AND product = " . SqlQuote($product)) if $product; + # Limit to a single product if requested + $query .= (" AND bugs.product_id = " . $product_id) if $product_id; SendSQL(SelectVisible($query, $userid, diff --git a/editattachstatuses.cgi b/editattachstatuses.cgi index 910379b99..ff7822181 100755 --- a/editattachstatuses.cgi +++ b/editattachstatuses.cgi @@ -155,7 +155,7 @@ sub validateSortKey sub validateProduct { # Retrieve a list of products. - SendSQL("SELECT product FROM products"); + SendSQL("SELECT name FROM products"); my @products; push(@products, FetchSQLData()) while MoreSQLData(); @@ -180,11 +180,13 @@ sub list # Retrieve a list of attachment status flags and create an array of hashes # in which each hash contains the data for one flag. - SendSQL("SELECT id, name, description, sortkey, product, count(statusid) - FROM attachstatusdefs LEFT JOIN attachstatuses - ON attachstatusdefs.id=attachstatuses.statusid - GROUP BY id - ORDER BY sortkey"); + SendSQL("SELECT attachstatusdefs.id, attachstatusdefs.name, " . + "attachstatusdefs.description, attachstatusdefs.sortkey, products.name, " . + "count(attachstatusdefs.id) " . + "FROM attachstatusdefs, products " . + "WHERE products.id = attachstatusdefs.product_id " . + "GROUP BY id " . + "ORDER BY attachstatusdefs.sortkey"); my @statusdefs; while ( MoreSQLData() ) { @@ -212,7 +214,7 @@ sub create # Display a form for creating a new attachment status flag. # Retrieve a list of products to which the attachment status may apply. - SendSQL("SELECT product FROM products"); + SendSQL("SELECT name FROM products"); my @products; push(@products, FetchSQLData()) while MoreSQLData(); @@ -236,14 +238,13 @@ sub insert # in a SQL statement. my $name = SqlQuote($::FORM{'name'}); my $desc = SqlQuote($::FORM{'desc'}); - my $product = SqlQuote($::FORM{'product'}); + my $product_id = get_product_id($::FORM{'product'}); SendSQL("LOCK TABLES attachstatusdefs WRITE"); SendSQL("SELECT MAX(id) FROM attachstatusdefs"); - my $id = FetchSQLData() || 0; - $id++; - SendSQL("INSERT INTO attachstatusdefs (id, name, description, sortkey, product) - VALUES ($id, $name, $desc, $::FORM{'sortkey'}, $product)"); + my $id = FetchSQLData() + 1; + SendSQL("INSERT INTO attachstatusdefs (id, name, description, sortkey, product_id) + VALUES ($id, $name, $desc, $::FORM{'sortkey'}, $product_id)"); SendSQL("UNLOCK TABLES"); # Display the "administer attachment status flags" page @@ -257,8 +258,11 @@ sub edit # Display a form for editing an existing attachment status flag. # Retrieve the definition from the database. - SendSQL("SELECT name, description, sortkey, product - FROM attachstatusdefs WHERE id = $::FORM{'id'}"); + SendSQL("SELECT attachstatusdefs.name, attachstatusdefs.description, " . + " attachstatusdefs.sortkey, products.name " . + "FROM attachstatusdefs, products " . + "WHERE attachstatusdefs.product_id = products.id " . + " AND attachstatusdefs.id = $::FORM{'id'}"); my ($name, $desc, $sortkey, $product) = FetchSQLData(); # Define the variables and functions that will be passed to the UI template. diff --git a/editcomponents.cgi b/editcomponents.cgi index 576f01375..3f1619739 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -55,9 +55,9 @@ sub TestProduct ($) my $prod = shift; # does the product exist? - SendSQL("SELECT product + SendSQL("SELECT name FROM products - WHERE product=" . SqlQuote($prod)); + WHERE name=" . SqlQuote($prod)); return FetchOneColumn(); } @@ -84,9 +84,10 @@ sub TestComponent ($$) my ($prod,$comp) = @_; # does the product exist? - SendSQL("SELECT program,value - FROM components - WHERE program=" . SqlQuote($prod) . " and value=" . SqlQuote($comp)); + SendSQL("SELECT components.name + FROM components, products + WHERE products.id = components.product_id + AND products.name=" . SqlQuote($prod) . " AND components.name=" . SqlQuote($comp)); return FetchOneColumn(); } @@ -225,15 +226,14 @@ unless ($product) { PutHeader("Select product"); if ($dobugcounts){ - SendSQL("SELECT products.product,products.description,COUNT(bug_id) - FROM products LEFT JOIN bugs - ON products.product=bugs.product - GROUP BY products.product - ORDER BY products.product"); + SendSQL("SELECT products.name,products.description,COUNT(bug_id) + FROM products LEFT JOIN bugs ON products.id = bugs.product_id + GROUP BY products.name + ORDER BY products.name"); } else { - SendSQL("SELECT products.product,products.description + SendSQL("SELECT products.name,products.description FROM products - ORDER BY products.product"); + ORDER BY products.name"); } print "\n"; print " \n"; @@ -270,18 +270,18 @@ unless ($product) { unless ($action) { PutHeader("Select component of $product"); CheckProduct($product); + my $product_id = get_product_id($product); if ($dobugcounts) { - SendSQL("SELECT value,description,initialowner,initialqacontact,COUNT(bug_id) - FROM components LEFT JOIN bugs - ON components.program=bugs.product AND components.value=bugs.component - WHERE program=" . SqlQuote($product) . " - GROUP BY value"); + SendSQL("SELECT name,description,initialowner,initialqacontact,COUNT(bug_id) + FROM components LEFT JOIN bugs ON components.id = bugs.component_id + WHERE components.product_id=$product_id + GROUP BY name"); } else { - SendSQL("SELECT value,description,initialowner,initialqacontact + SendSQL("SELECT name,description,initialowner,initialqacontact FROM components - WHERE program=" . SqlQuote($product) . " - GROUP BY value"); + WHERE product_id=$product_id + GROUP BY name"); } print "
Edit components of ...
\n"; print " \n"; @@ -370,6 +370,7 @@ if ($action eq 'add') { if ($action eq 'new') { PutHeader("Adding new component of $product"); CheckProduct($product); + my $product_id = get_product_id($product); # Cleanups and valididy checks @@ -426,9 +427,9 @@ if ($action eq 'new') { # Add the new component SendSQL("INSERT INTO components ( " . - "program, value, description, initialowner, initialqacontact " . + "product_id, name, description, initialowner, initialqacontact " . " ) VALUES ( " . - SqlQuote($product) . "," . + $product_id . "," . SqlQuote($component) . "," . SqlQuote($description) . "," . SqlQuote($initialownerid) . "," . @@ -457,20 +458,20 @@ if ($action eq 'new') { if ($action eq 'del') { PutHeader("Delete component of $product"); CheckComponent($product, $component); + my $component_id = get_component_id(get_product_id($product), $component); # display some data about the component - SendSQL("SELECT products.product,products.description, + SendSQL("SELECT products.name,products.description, products.milestoneurl,products.disallownew, - components.program,components.value,components.initialowner, + components.name,components.initialowner, components.initialqacontact,components.description FROM products - LEFT JOIN components on product=program - WHERE product=" . SqlQuote($product) . " - AND value=" . SqlQuote($component) ); + LEFT JOIN components ON products.id = components.product_id + WHERE components.id = $component_id"); my ($product,$pdesc,$milestoneurl,$disallownew, - $dummy,$component,$initialownerid,$initialqacontactid,$cdesc) = FetchSQLData(); + $component,$initialownerid,$initialqacontactid,$cdesc) = FetchSQLData(); my $initialowner = $initialownerid ? DBID_to_name ($initialownerid) : "missing"; my $initialqacontact = $initialqacontactid ? DBID_to_name ($initialqacontactid) : "missing"; @@ -503,8 +504,7 @@ if ($action eq 'del') { } SendSQL("SELECT count(bug_id) FROM bugs - WHERE product=" . SqlQuote($product) . " - AND component=" . SqlQuote($component)); + WHERE component_id = $component_id"); print "\n"; print " \n"; @@ -574,6 +574,7 @@ one."; if ($action eq 'delete') { PutHeader("Deleting component of $product"); CheckComponent($product,$component); + my $component_id = get_component_id(get_product_id($product),$component); # lock the tables before we start to change everything: @@ -590,8 +591,7 @@ if ($action eq 'delete') { if (Param("allowbugdeletion")) { SendSQL("SELECT bug_id FROM bugs - WHERE product=" . SqlQuote($product) . " - AND component=" . SqlQuote($component)); + WHERE component_id=$component_id"); while (MoreSQLData()) { my $bugid = FetchOneColumn(); @@ -607,14 +607,12 @@ if ($action eq 'delete') { # Deleting the rest is easier: SendSQL("DELETE FROM bugs - WHERE product=" . SqlQuote($product) . " - AND component=" . SqlQuote($component)); + WHERE component_id=$component_id"); print "Bugs deleted.
\n"; } SendSQL("DELETE FROM components - WHERE program=" . SqlQuote($product) . " - AND value=" . SqlQuote($component)); + WHERE id=$component_id"); print "Components deleted.

\n"; SendSQL("UNLOCK TABLES"); @@ -634,19 +632,18 @@ if ($action eq 'delete') { if ($action eq 'edit') { PutHeader("Edit component of $product"); CheckComponent($product,$component); + my $component_id = get_component_id(get_product_id($product),$component); # get data of component - SendSQL("SELECT products.product,products.description, + SendSQL("SELECT products.name,products.description, products.milestoneurl,products.disallownew, - components.program,components.value,components.initialowner, + components.name,components.initialowner, components.initialqacontact,components.description - FROM products - LEFT JOIN components on product=program - WHERE product=" . SqlQuote($product) . " - AND value=" . SqlQuote($component) ); + FROM products LEFT JOIN components ON products.id = components.product_id + WHERE components.id = $component_id"); my ($product,$pdesc,$milestoneurl,$disallownew, - $dummy,$component,$initialownerid,$initialqacontactid,$cdesc) = FetchSQLData(); + $component,$initialownerid,$initialqacontactid,$cdesc) = FetchSQLData(); my $initialowner = $initialownerid ? DBID_to_name ($initialownerid) : ''; my $initialqacontact = $initialqacontactid ? DBID_to_name ($initialqacontactid) : ''; @@ -663,8 +660,7 @@ if ($action eq 'edit') { print "

Edit component ...
Component of product:"; SendSQL("SELECT count(*) FROM bugs - WHERE product=" . SqlQuote($product) . - " and component=" . SqlQuote($component)); + WHERE component_id=$component_id"); my $bugs = ''; $bugs = FetchOneColumn() if MoreSQLData(); print $bugs || 'none'; @@ -707,13 +703,13 @@ if ($action eq 'update') { my $initialqacontact = trim($::FORM{initialqacontact} || ''); my $initialqacontactold = trim($::FORM{initialqacontactold} || ''); - CheckComponent($product,$componentold); - # Note that the order of this tests is important. If you change # them, be sure to test for WHERE='$component' or WHERE='$componentold' - SendSQL("LOCK TABLES bugs WRITE, - components WRITE, profiles READ"); + SendSQL("LOCK TABLES components WRITE, products READ, profiles READ"); + CheckComponent($product,$componentold); + my $component_id = get_component_id(get_product_id($product), + $componentold); if ($description ne $descriptionold) { unless ($description) { @@ -724,8 +720,7 @@ if ($action eq 'update') { } SendSQL("UPDATE components SET description=" . SqlQuote($description) . " - WHERE program=" . SqlQuote($product) . " - AND value=" . SqlQuote($componentold)); + WHERE id=$component_id"); print "Updated description.
\n"; } @@ -748,8 +743,7 @@ if ($action eq 'update') { SendSQL("UPDATE components SET initialowner=" . SqlQuote($initialownerid) . " - WHERE program=" . SqlQuote($product) . " - AND value=" . SqlQuote($componentold)); + WHERE id = $component_id"); print "Updated initial owner.
\n"; } @@ -764,8 +758,7 @@ if ($action eq 'update') { SendSQL("UPDATE components SET initialqacontact=" . SqlQuote($initialqacontactid) . " - WHERE program=" . SqlQuote($product) . " - AND value=" . SqlQuote($componentold)); + WHERE id = $component_id"); print "Updated initial QA contact.
\n"; } @@ -784,15 +777,8 @@ if ($action eq 'update') { exit; } - SendSQL("UPDATE bugs - SET component=" . SqlQuote($component) . ", - delta_ts = delta_ts - WHERE component=" . SqlQuote($componentold) . " - AND product=" . SqlQuote($product)); - SendSQL("UPDATE components - SET value=" . SqlQuote($component) . " - WHERE value=" . SqlQuote($componentold) . " - AND program=" . SqlQuote($product)); + SendSQL("UPDATE components SET name=" . SqlQuote($component) . + "WHERE id=$component_id"); unlink "data/versioncache"; print "Updated component name.
\n"; diff --git a/editgroups.cgi b/editgroups.cgi index 9c93363c0..623cf47d3 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -422,7 +422,7 @@ this box. It is strongly suggested that you review the bugs in this group before checking the box.

"; } - SendSQL("SELECT product FROM products WHERE product=" . SqlQuote($name)); + SendSQL("SELECT name FROM products WHERE name=" . SqlQuote($name)); if (MoreSQLData()) { $cantdelete = 1; print " @@ -489,7 +489,7 @@ if ($action eq 'delete') { $cantdelete = 1; } } - SendSQL("SELECT product FROM products WHERE product=" . SqlQuote($name)); + SendSQL("SELECT name FROM products WHERE name=" . SqlQuote($name)); if (FetchOneColumn()) { if (!defined $::FORM{'unbind'}) { $cantdelete = 1; diff --git a/editmilestones.cgi b/editmilestones.cgi index fccf72533..67d84fcce 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -33,9 +33,9 @@ sub TestProduct ($) my $prod = shift; # does the product exist? - SendSQL("SELECT product + SendSQL("SELECT name FROM products - WHERE product=" . SqlQuote($prod)); + WHERE name=" . SqlQuote($prod)); return FetchOneColumn(); } @@ -62,9 +62,9 @@ sub TestMilestone ($$) my ($prod,$mile) = @_; # does the product exist? - SendSQL("SELECT product,value - FROM milestones - WHERE product=" . SqlQuote($prod) . " and value=" . SqlQuote($mile)); + SendSQL("SELECT products.name, value + FROM milestones, products + WHERE milestones.product_id=products.id AND products.name=" . SqlQuote($prod) . " and value=" . SqlQuote($mile)); return FetchOneColumn(); } @@ -183,10 +183,10 @@ if ($milestone) { unless ($product) { PutHeader("Select product"); - SendSQL("SELECT products.product,products.description,'xyzzy' + SendSQL("SELECT products.name,products.description,'xyzzy' FROM products - GROUP BY products.product - ORDER BY products.product"); + GROUP BY products.name + ORDER BY products.name"); print "\n"; print " \n"; print " \n"; @@ -216,10 +216,11 @@ unless ($product) { unless ($action) { PutHeader("Select milestone for $product"); CheckProduct($product); + my $product_id = get_product_id($product); SendSQL("SELECT value,sortkey FROM milestones - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id ORDER BY sortkey,value"); print "
Edit milestones of ...Description
\n"; @@ -259,6 +260,7 @@ unless ($action) { if ($action eq 'add') { PutHeader("Add milestone for $product"); CheckProduct($product); + my $product_id = get_product_id($product); #print "This page lets you add a new milestone to a $::bugzilla_name tracked product.\n"; @@ -287,6 +289,7 @@ if ($action eq 'add') { if ($action eq 'new') { PutHeader("Adding new milestone for $product"); CheckProduct($product); + my $product_id = get_product_id($product); # Cleanups and valididy checks @@ -305,10 +308,9 @@ if ($action eq 'new') { # Add the new milestone SendSQL("INSERT INTO milestones ( " . - "value, product, sortkey" . + "value, product_id, sortkey" . " ) VALUES ( " . - SqlQuote($milestone) . "," . - SqlQuote($product) . ", $sortkey)"); + SqlQuote($milestone) . ", $product_id, $sortkey)"); # Make versioncache flush unlink "data/versioncache"; @@ -330,16 +332,17 @@ if ($action eq 'new') { if ($action eq 'del') { PutHeader("Delete milestone of $product"); CheckMilestone($product, $milestone); + my $product_id = get_product_id($product); - SendSQL("SELECT count(bug_id),product,target_milestone + SendSQL("SELECT count(bug_id), product_id, target_milestone FROM bugs - GROUP BY product,target_milestone - HAVING product=" . SqlQuote($product) . " + GROUP BY product_id, target_milestone + HAVING product_id=$product_id AND target_milestone=" . SqlQuote($milestone)); my $bugs = FetchOneColumn(); SendSQL("SELECT defaultmilestone FROM products " . - "WHERE product=" . SqlQuote($product)); + "WHERE id=$product_id"); my $defaultmilestone = FetchOneColumn(); print "
\n"; @@ -405,6 +408,7 @@ one."; if ($action eq 'delete') { PutHeader("Deleting milestone of $product"); CheckMilestone($product,$milestone); + my $product_id = get_product_id($product); # lock the tables before we start to change everything: @@ -422,7 +426,7 @@ if ($action eq 'delete') { SendSQL("SELECT bug_id FROM bugs - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id AND target_milestone=" . SqlQuote($milestone)); while (MoreSQLData()) { my $bugid = FetchOneColumn(); @@ -439,13 +443,13 @@ if ($action eq 'delete') { # Deleting the rest is easier: SendSQL("DELETE FROM bugs - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id AND target_milestone=" . SqlQuote($milestone)); print "Bugs deleted.
\n"; } SendSQL("DELETE FROM milestones - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id AND value=" . SqlQuote($milestone)); print "Milestone deleted.

\n"; SendSQL("UNLOCK TABLES"); @@ -466,9 +470,10 @@ if ($action eq 'delete') { if ($action eq 'edit') { PutHeader("Edit milestone of $product"); CheckMilestone($product,$milestone); + my $product_id = get_product_id($product); - SendSQL("SELECT sortkey FROM milestones WHERE product=" . - SqlQuote($product) . " AND value = " . SqlQuote($milestone)); + SendSQL("SELECT sortkey FROM milestones WHERE product_id=$product_id " . + " AND value = " . SqlQuote($milestone)); my $sortkey = FetchOneColumn(); print "\n"; @@ -506,6 +511,7 @@ if ($action eq 'update') { my $sortkeyold = trim($::FORM{sortkeyold} || '0'); CheckMilestone($product,$milestoneold); + my $product_id = get_product_id($product); SendSQL("LOCK TABLES bugs WRITE, milestones WRITE, @@ -535,14 +541,14 @@ if ($action eq 'update') { SET target_milestone=" . SqlQuote($milestone) . ", delta_ts=delta_ts WHERE target_milestone=" . SqlQuote($milestoneold) . " - AND product=" . SqlQuote($product)); + AND product_id=$product_id"); SendSQL("UPDATE milestones SET value=" . SqlQuote($milestone) . " - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id AND value=" . SqlQuote($milestoneold)); SendSQL("UPDATE products " . "SET defaultmilestone = " . SqlQuote($milestone) . - "WHERE product = " . SqlQuote($product) . + " WHERE id = $product_id" . " AND defaultmilestone = " . SqlQuote($milestoneold)); unlink "data/versioncache"; print "Updated milestone.
\n"; diff --git a/editproducts.cgi b/editproducts.cgi index 1b3441a8a..4b0698b35 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -51,9 +51,9 @@ sub TestProduct ($) my $prod = shift; # does the product exist? - SendSQL("SELECT product + SendSQL("SELECT name FROM products - WHERE product=" . SqlQuote($prod)); + WHERE name=" . SqlQuote($prod)); return FetchOneColumn(); } @@ -206,12 +206,11 @@ my $localtrailer = "edit more products"; unless ($action) { PutHeader("Select product"); - SendSQL("SELECT products.product,description,disallownew, + SendSQL("SELECT products.name,description,disallownew, votesperuser,maxvotesperbug,votestoconfirm,COUNT(bug_id) - FROM products LEFT JOIN bugs - ON products.product=bugs.product - GROUP BY products.product - ORDER BY products.product"); + FROM products LEFT JOIN bugs ON products.id = bugs.product_id + GROUP BY products.name + ORDER BY products.name"); print "

\n"; print " \n"; print " \n"; @@ -330,7 +329,7 @@ if ($action eq 'new') { # Add the new product. SendSQL("INSERT INTO products ( " . - "product, description, milestoneurl, disallownew, votesperuser, " . + "name, description, milestoneurl, disallownew, votesperuser, " . "maxvotesperbug, votestoconfirm, defaultmilestone" . " ) VALUES ( " . SqlQuote($product) . "," . @@ -339,14 +338,16 @@ if ($action eq 'new') { $disallownew . "," . "$votesperuser, $maxvotesperbug, $votestoconfirm, " . SqlQuote($defaultmilestone) . ")"); + SendSQL("SELECT LAST_INSERT_ID()"); + my $product_id = FetchOneColumn(); SendSQL("INSERT INTO versions ( " . - "value, program" . + "value, product_id" . " ) VALUES ( " . SqlQuote($version) . "," . - SqlQuote($product) . ")" ); + $product_id . ")" ); - SendSQL("INSERT INTO milestones (product, value) VALUES (" . - SqlQuote($product) . ", " . SqlQuote($defaultmilestone) . ")"); + SendSQL("INSERT INTO milestones (product_id, value) VALUES (" . + $product_id . ", " . SqlQuote($defaultmilestone) . ")"); # If we're using bug groups, then we need to create a group for this # product as well. -JMR, 2/16/00 @@ -416,10 +417,10 @@ if ($action eq 'del') { CheckProduct($product); # display some data about the product - SendSQL("SELECT description, milestoneurl, disallownew + SendSQL("SELECT product_id, description, milestoneurl, disallownew FROM products - WHERE product=" . SqlQuote($product)); - my ($description, $milestoneurl, $disallownew) = FetchSQLData(); + WHERE name=" . SqlQuote($product)); + my ($product_id, $description, $milestoneurl, $disallownew) = FetchSQLData(); my $milestonelink = $milestoneurl ? "$milestoneurl" : "missing"; $description ||= "description missing"; @@ -468,9 +469,9 @@ if ($action eq 'del') { print "\n"; print " \n"; print " \n"; print " + [% FOREACH column = display_columns %] + [% NEXT IF column == group_field || excluded_columns.contains(column) %] + + [% END %] + + [% END %] +
Edit product ...Description
Components:"; - SendSQL("SELECT value,description + SendSQL("SELECT name,description FROM components - WHERE program=" . SqlQuote($product)); + WHERE product_id=$product_id"); if (MoreSQLData()) { print ""; while ( MoreSQLData() ) { @@ -489,7 +490,7 @@ if ($action eq 'del') { print " \n\n"; print " \n"; print " \n"; print " \n"; print " "; print ""; print ""; - print ""; + print ""; print ""; print "\n"; print "\n"; @@ -223,17 +297,17 @@ if ($action eq 'add') { print "

"; print "Name is what is used with the UserInGroup() function in any customized cgi files you write that use a given group. It can also be used by -people submitting bugs by email to limit a bug to a certain groupset. It +people submitting bugs by email to limit a bug to a certain set of groups. It may not contain any spaces.

"; print "Description is what will be shown in the bug reports to members of the group where they can choose whether the bug will be restricted to others in the same group.

"; - print "The Active flag determines whether or not the group is active. -If you deactivate a group it will no longer be possible for users to add bugs -to that group, although bugs already in the group will remain in the group. -Deactivating a group is a much less drastic way to stop a group from growing + print "The Use For Bugs flag determines whether or not the group is eligible to be used for bugs. +If you clear this, it will no longer be possible for users to add bugs +to this group, although bugs already in the group will remain in the group. +Doing so is a much less drastic way to stop a group from growing than deleting the group would be. Note: If you are creating a group, you -probably want it to be active, in which case you should leave this checked.

"; +probably want it to be usable for bugs, in which case you should leave this checked.

"; print "User RegExp is optional, and if filled in, will automatically grant membership to this group to anyone creating a new account with an email address that matches this regular expression.

"; @@ -287,62 +361,30 @@ if ($action eq 'new') { exit; } - # Major hack for bit values... perl can't handle 64-bit ints, so I can't - # just do the math to get the next available bit number, gotta handle - # them as strings... also, we're actually only going to allow 63 bits - # because that's all that opblessgroupset masks for (the high bit is off - # to avoid signing issues). - - my @bitvals = ('1','2','4','8','16','32','64','128','256','512','1024', - '2048','4096','8192','16384','32768', - - '65536','131072','262144','524288','1048576','2097152', - '4194304','8388608','16777216','33554432','67108864', - '134217728','268435456','536870912','1073741824', - '2147483648', - - '4294967296','8589934592','17179869184','34359738368', - '68719476736','137438953472','274877906944', - '549755813888','1099511627776','2199023255552', - '4398046511104','8796093022208','17592186044416', - '35184372088832','70368744177664','140737488355328', - - '281474976710656','562949953421312','1125899906842624', - '2251799813685248','4503599627370496','9007199254740992', - '18014398509481984','36028797018963968','72057594037927936', - '144115188075855872','288230376151711744', - '576460752303423488','1152921504606846976', - '2305843009213693952','4611686018427387904'); - - # First the next available bit - my $bit = ""; - foreach (@bitvals) { - if ($bit eq "") { - SendSQL("SELECT bit FROM groups WHERE bit=" . SqlQuote($_)); - if (!FetchOneColumn()) { $bit = $_; } - } - } - if ($bit eq "") { - ShowError("Sorry, you already have the maximum number of groups " . - "defined.

You must delete a group first before you " . - "can add any more."); - PutTrailer("Back to the group list"); + if (!eval {qr/$regexp/}) { + ShowError("The regular expression you entered is invalid. " . + "Please click the Back button and try again."); + PutFooter(); exit; } # Add the new group SendSQL("INSERT INTO groups ( " . - "bit, name, description, isbuggroup, userregexp, isactive" . + "name, description, isbuggroup, userregexp, isactive, last_changed " . " ) VALUES ( " . - $bit . "," . - SqlQuote($name) . "," . - SqlQuote($desc) . "," . + SqlQuote($name) . ", " . + SqlQuote($desc) . ", " . "1," . - SqlQuote($regexp) . "," . - $isactive . ")" ); - + SqlQuote($regexp) . ", " . + $isactive . ", NOW())" ); + SendSQL("SELECT last_insert_id()"); + my $gid = FetchOneColumn(); + my $admin = GroupNameToId('admin'); + SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless) + VALUES ($admin, $gid, 0)"); + SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless) + VALUES ($admin, $gid, 1)"); print "OK, done.

\n"; - print "Your new group was assigned bit #$bit.

"; PutTrailer("Add another group", "Back to the group list"); exit; @@ -356,14 +398,14 @@ if ($action eq 'new') { if ($action eq 'del') { PutHeader("Delete group"); - my $bit = trim($::FORM{group} || ''); - unless ($bit) { + my $gid = trim($::FORM{group} || ''); + unless ($gid) { ShowError("No group specified.
" . "Click the Back button and try again."); PutFooter(); exit; } - SendSQL("SELECT bit FROM groups WHERE bit=" . SqlQuote($bit)); + SendSQL("SELECT id FROM groups WHERE id=" . SqlQuote($gid)); if (!FetchOneColumn()) { ShowError("That group doesn't exist.
" . "Click the Back button and try again."); @@ -372,17 +414,17 @@ if ($action eq 'del') { } SendSQL("SELECT name,description " . "FROM groups " . - "WHERE bit = " . SqlQuote($bit)); + "WHERE id = " . SqlQuote($gid)); my ($name, $desc) = FetchSQLData(); print "

"; SendSQL("SELECT value FROM versions - WHERE program=" . SqlQuote($product) . " + WHERE product_id=$product_id ORDER BY value"); if (MoreSQLData()) { my $br = 0; @@ -512,7 +513,7 @@ if ($action eq 'del') { print " "; SendSQL("SELECT value FROM milestones - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id ORDER BY sortkey,value"); if(MoreSQLData()) { my $br = 0; @@ -530,10 +531,10 @@ if ($action eq 'del') { print "
Bugs:"; - SendSQL("SELECT count(bug_id),product + SendSQL("SELECT count(bug_id),product_id FROM bugs - GROUP BY product - HAVING product=" . SqlQuote($product)); + GROUP BY product_id + HAVING product_id=$product_id"); my $bugs = FetchOneColumn(); print $bugs || 'none'; @@ -578,6 +579,7 @@ one."; if ($action eq 'delete') { PutHeader("Deleting product"); CheckProduct($product); + my $product_id = get_product_id($product); # lock the tables before we start to change everything: @@ -599,7 +601,7 @@ if ($action eq 'delete') { if (Param("allowbugdeletion")) { SendSQL("SELECT bug_id FROM bugs - WHERE product=" . SqlQuote($product)); + WHERE product_id=$product_id"); while (MoreSQLData()) { my $bugid = FetchOneColumn(); @@ -615,25 +617,25 @@ if ($action eq 'delete') { # Deleting the rest is easier: SendSQL("DELETE FROM bugs - WHERE product=" . SqlQuote($product)); + WHERE product_id=$product_id"); print "Bugs deleted.
\n"; } SendSQL("DELETE FROM components - WHERE program=" . SqlQuote($product)); + WHERE product_id=$product_id"); print "Components deleted.
\n"; SendSQL("DELETE FROM versions - WHERE program=" . SqlQuote($product)); + WHERE product_id=$product_id"); print "Versions deleted.

\n"; # deleting associated target milestones - matthew@zeroknowledge.com SendSQL("DELETE FROM milestones - WHERE product=" . SqlQuote($product)); + WHERE product_id=$product_id"); print "Milestones deleted.
\n"; SendSQL("DELETE FROM products - WHERE product=" . SqlQuote($product)); + WHERE product_id=$product_id"); print "Product '$product' deleted.
\n"; # Added -JMR, 2/16/00 @@ -681,11 +683,11 @@ if ($action eq 'edit') { CheckProduct($product); # get data of product - SendSQL("SELECT description,milestoneurl,disallownew, + SendSQL("SELECT id,description,milestoneurl,disallownew, votesperuser,maxvotesperbug,votestoconfirm,defaultmilestone FROM products - WHERE product=" . SqlQuote($product)); - my ($description, $milestoneurl, $disallownew, + WHERE name=" . SqlQuote($product)); + my ($product_id,$description, $milestoneurl, $disallownew, $votesperuser, $maxvotesperbug, $votestoconfirm, $defaultmilestone) = FetchSQLData(); @@ -707,9 +709,9 @@ if ($action eq 'edit') { print "

Edit components:"; - SendSQL("SELECT value,description + SendSQL("SELECT name,description FROM components - WHERE program=" . SqlQuote($product)); + WHERE product_id=$product_id"); if (MoreSQLData()) { print ""; while ( MoreSQLData() ) { @@ -729,7 +731,7 @@ if ($action eq 'edit') { print " \n\n"; print " \n"; print "
"; SendSQL("SELECT value FROM versions - WHERE program=" . SqlQuote($product) . " + WHERE product_id=$product_id ORDER BY value"); if (MoreSQLData()) { my $br = 0; @@ -752,7 +754,7 @@ if ($action eq 'edit') { print " "; SendSQL("SELECT value FROM milestones - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id ORDER BY sortkey,value"); if(MoreSQLData()) { my $br = 0; @@ -770,10 +772,10 @@ if ($action eq 'edit') { print "
Bugs:"; - SendSQL("SELECT count(bug_id),product + SendSQL("SELECT count(bug_id),product_id FROM bugs - GROUP BY product - HAVING product=" . SqlQuote($product)); + GROUP BY product_id + HAVING product_id=$product_id"); my $bugs = ''; $bugs = FetchOneColumn() if MoreSQLData(); print $bugs || 'none'; @@ -837,6 +839,7 @@ if ($action eq 'update') { my $checkvotes = 0; CheckProduct($productold); + my $product_id = get_product_id($productold); if ($maxvotesperbug !~ /^\d+$/ || $maxvotesperbug <= 0) { print "Sorry, the max votes per bug must be a positive integer."; @@ -844,22 +847,20 @@ if ($action eq 'update') { exit; } - # Note that the order of this tests is important. If you change - # them, be sure to test for WHERE='$product' or WHERE='$productold' + # Note that we got the $product_id using $productold above so it will + # remain static even after we rename the product in the database. - SendSQL("LOCK TABLES bugs WRITE, - components WRITE, - products WRITE, - versions WRITE, + SendSQL("LOCK TABLES products WRITE, + versions READ, groups WRITE, profiles WRITE, - milestones WRITE"); + milestones READ"); if ($disallownew ne $disallownewold) { $disallownew ||= 0; SendSQL("UPDATE products SET disallownew=$disallownew - WHERE product=" . SqlQuote($productold)); + WHERE id=$product_id"); print "Updated bug submit status.
\n"; } @@ -872,14 +873,14 @@ if ($action eq 'update') { } SendSQL("UPDATE products SET description=" . SqlQuote($description) . " - WHERE product=" . SqlQuote($productold)); + WHERE id=$product_id"); print "Updated description.
\n"; } if (Param('usetargetmilestone') && $milestoneurl ne $milestoneurlold) { SendSQL("UPDATE products SET milestoneurl=" . SqlQuote($milestoneurl) . " - WHERE product=" . SqlQuote($productold)); + WHERE id=$product_id"); print "Updated mile stone URL.
\n"; } @@ -949,7 +950,7 @@ if ($action eq 'update') { if ($votesperuser ne $votesperuserold) { SendSQL("UPDATE products SET votesperuser=$votesperuser - WHERE product=" . SqlQuote($productold)); + WHERE id=$product_id"); print "Updated votes per user.
\n"; $checkvotes = 1; } @@ -958,7 +959,7 @@ if ($action eq 'update') { if ($maxvotesperbug ne $maxvotesperbugold) { SendSQL("UPDATE products SET maxvotesperbug=$maxvotesperbug - WHERE product=" . SqlQuote($productold)); + WHERE id=$product_id"); print "Updated max votes per bug.
\n"; $checkvotes = 1; } @@ -967,7 +968,7 @@ if ($action eq 'update') { if ($votestoconfirm ne $votestoconfirmold) { SendSQL("UPDATE products SET votestoconfirm=$votestoconfirm - WHERE product=" . SqlQuote($productold)); + WHERE id=$product_id"); print "Updated votes to confirm.
\n"; $checkvotes = 1; } @@ -976,7 +977,7 @@ if ($action eq 'update') { if ($defaultmilestone ne $defaultmilestoneold) { SendSQL("SELECT value FROM milestones " . "WHERE value = " . SqlQuote($defaultmilestone) . - " AND product = " . SqlQuote($productold)); + " AND product_id = $product_id"); if (!FetchOneColumn()) { print "Sorry, the milestone $defaultmilestone must be defined first."; SendSQL("UNLOCK TABLES"); @@ -985,7 +986,7 @@ if ($action eq 'update') { } SendSQL("UPDATE products " . "SET defaultmilestone = " . SqlQuote($defaultmilestone) . - "WHERE product=" . SqlQuote($productold)); + "WHERE id=$product_id"); print "Updated default milestone.
\n"; } @@ -1006,11 +1007,7 @@ if ($action eq 'update') { exit; } - SendSQL("UPDATE bugs SET product=$qp, delta_ts=delta_ts WHERE product=$qpold"); - SendSQL("UPDATE components SET program=$qp WHERE program=$qpold"); - SendSQL("UPDATE products SET product=$qp WHERE product=$qpold"); - SendSQL("UPDATE versions SET program=$qp WHERE program=$qpold"); - SendSQL("UPDATE milestones SET product=$qp WHERE product=$qpold"); + SendSQL("UPDATE products SET name=$qp WHERE id=$product_id"); # Need to do an update to groups as well. If there is a corresponding # bug group, whether usebuggroups is currently set or not, we want to # update it so it will match in the future. If there is no group, this @@ -1031,7 +1028,7 @@ if ($action eq 'update') { SendSQL("SELECT votes.who, votes.bug_id " . "FROM votes, bugs " . "WHERE bugs.bug_id = votes.bug_id " . - " AND bugs.product = $qp " . + " AND bugs.product_id = $product_id " . " AND votes.count > $maxvotesperbug"); my @list; while (MoreSQLData()) { @@ -1047,7 +1044,7 @@ if ($action eq 'update') { } SendSQL("SELECT votes.who, votes.count FROM votes, bugs " . "WHERE bugs.bug_id = votes.bug_id " . - " AND bugs.product = $qp"); + " AND bugs.product_id = $product_id"); my %counts; while (MoreSQLData()) { my ($who, $count) = (FetchSQLData()); @@ -1061,7 +1058,7 @@ if ($action eq 'update') { if ($counts{$who} > $votesperuser) { SendSQL("SELECT votes.bug_id FROM votes, bugs " . "WHERE bugs.bug_id = votes.bug_id " . - " AND bugs.product = $qp " . + " AND bugs.product_id = $product_id " . " AND votes.who = $who"); while (MoreSQLData()) { my $id = FetchSQLData(); @@ -1073,7 +1070,7 @@ if ($action eq 'update') { } } SendSQL("SELECT bug_id FROM bugs " . - "WHERE product = $qp " . + "WHERE product_id = $product_id " . " AND bug_status = '$::unconfirmedstate' " . " AND votes >= $votestoconfirm"); my @list; diff --git a/editusers.cgi b/editusers.cgi index f9800c704..0e25f0ac3 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -579,9 +579,10 @@ if ($action eq 'del') { # Check if the user is an initialowner my $nodelete = ''; - SendSQL("SELECT program, value - FROM components - WHERE initialowner=" . DBname_to_id($user)); + SendSQL("SELECT products.name, components.name " . + "FROM products, components " . + "WHERE products.id = components.product_id " . + " AND initialowner=" . DBname_to_id($user)); $found = 0; while (MoreSQLData()) { if ($found) { @@ -603,9 +604,10 @@ if ($action eq 'del') { # Check if the user is an initialqacontact - SendSQL("SELECT program, value - FROM components - WHERE initialqacontact=" . DBname_to_id($user)); + SendSQL("SELECT products.name, components.name " . + "FROM products, components " . + "WHERE products.id = components.id " . + " AND initialqacontact=" . DBname_to_id($user)); $found = 0; while (MoreSQLData()) { if ($found) { diff --git a/editversions.cgi b/editversions.cgi index 950d597a7..abeed2570 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -46,9 +46,9 @@ sub TestProduct ($) my $prod = shift; # does the product exist? - SendSQL("SELECT product + SendSQL("SELECT name FROM products - WHERE product=" . SqlQuote($prod)); + WHERE name=" . SqlQuote($prod)); return FetchOneColumn(); } @@ -75,9 +75,9 @@ sub TestVersion ($$) my ($prod,$ver) = @_; # does the product exist? - SendSQL("SELECT program,value - FROM versions - WHERE program=" . SqlQuote($prod) . " and value=" . SqlQuote($ver)); + SendSQL("SELECT products.name,value + FROM versions, products + WHERE versions.product_id=products.id AND products.name=" . SqlQuote($prod) . " and value=" . SqlQuote($ver)); return FetchOneColumn(); } @@ -191,10 +191,10 @@ if ($version) { unless ($product) { PutHeader("Select product"); - SendSQL("SELECT products.product,products.description,'xyzzy' + SendSQL("SELECT products.name,products.description,'xyzzy' FROM products - GROUP BY products.product - ORDER BY products.product"); + GROUP BY products.name + ORDER BY products.name"); print "\n"; print " \n"; print " \n"; @@ -217,8 +217,6 @@ unless ($product) { exit; } - - # # action='' -> Show nice list of versions # @@ -226,24 +224,11 @@ unless ($product) { unless ($action) { PutHeader("Select version of $product"); CheckProduct($product); + my $product_id = get_product_id($product); -=for me - - # Das geht nicht wie vermutet. Ich bekomme nicht alle Versionen - # angezeigt! Schade. Ich wrde gerne sehen, wieviel Bugs pro - # Version angegeben sind ... - - SendSQL("SELECT value,program,COUNT(bug_id) - FROM versions LEFT JOIN bugs - ON program=product AND value=version - WHERE program=" . SqlQuote($product) . " - GROUP BY value"); - -=cut - - SendSQL("SELECT value,program - FROM versions - WHERE program=" . SqlQuote($product) . " + SendSQL("SELECT value + FROM versions + WHERE product_id=$product_id ORDER BY value"); print "
Edit versions of ...Description
\n"; @@ -252,8 +237,7 @@ unless ($action) { print " \n"; print ""; while ( MoreSQLData() ) { - my ($version,$dummy,$bugs) = FetchSQLData(); - $bugs ||= 'none'; + my $version = FetchOneColumn(); print "\n"; print " \n"; #print " \n"; @@ -281,6 +265,7 @@ unless ($action) { if ($action eq 'add') { PutHeader("Add version of $product"); CheckProduct($product); + my $product_id = get_product_id($product); #print "This page lets you add a new version to a bugzilla-tracked product.\n"; @@ -309,6 +294,7 @@ if ($action eq 'add') { if ($action eq 'new') { PutHeader("Adding new version"); CheckProduct($product); + my $product_id = get_product_id($product); # Cleanups and valididy checks @@ -327,10 +313,9 @@ if ($action eq 'new') { # Add the new version SendSQL("INSERT INTO versions ( " . - "value, program" . + "value, product_id" . " ) VALUES ( " . - SqlQuote($version) . "," . - SqlQuote($product) . ")"); + SqlQuote($version) . ", $product_id)"); # Make versioncache flush unlink "data/versioncache"; @@ -352,12 +337,12 @@ if ($action eq 'new') { if ($action eq 'del') { PutHeader("Delete version of $product"); CheckVersion($product, $version); + my $product_id = get_product_id($product); - SendSQL("SELECT count(bug_id),product,version + SendSQL("SELECT count(bug_id) FROM bugs - GROUP BY product,version - HAVING product=" . SqlQuote($product) . " - AND version=" . SqlQuote($version)); + WHERE product_id = $product_id + AND version = " . SqlQuote($version)); my $bugs = FetchOneColumn(); print "
Action
$version$bugs
\n"; @@ -416,6 +401,7 @@ one."; if ($action eq 'delete') { PutHeader("Deleting version of $product"); CheckVersion($product,$version); + my $product_id = get_product_id($product); # lock the tables before we start to change everything: @@ -433,7 +419,7 @@ if ($action eq 'delete') { SendSQL("SELECT bug_id FROM bugs - WHERE product=" . SqlQuote($product) . " + WHERE product_id=$product_id AND version=" . SqlQuote($version)); while (MoreSQLData()) { my $bugid = FetchOneColumn(); @@ -450,13 +436,13 @@ if ($action eq 'delete') { # Deleting the rest is easier: SendSQL("DELETE FROM bugs - WHERE product=" . SqlQuote($product) . " + WHERE product_id = $product_id AND version=" . SqlQuote($version)); print "Bugs deleted.
\n"; } SendSQL("DELETE FROM versions - WHERE program=" . SqlQuote($product) . " + WHERE product_id = $product_id AND value=" . SqlQuote($version)); print "Version deleted.

\n"; SendSQL("UNLOCK TABLES"); @@ -477,6 +463,7 @@ if ($action eq 'delete') { if ($action eq 'edit') { PutHeader("Edit version of $product"); CheckVersion($product,$version); + my $product_id = get_product_id($product); print "\n"; print "

\n"; @@ -510,12 +497,14 @@ if ($action eq 'update') { my $versionold = trim($::FORM{versionold} || ''); CheckVersion($product,$versionold); + my $product_id = get_product_id($product); # Note that the order of this tests is important. If you change # them, be sure to test for WHERE='$version' or WHERE='$versionold' SendSQL("LOCK TABLES bugs WRITE, - versions WRITE"); + versions WRITE, + products READ"); if ($version ne $versionold) { unless ($version) { @@ -534,10 +523,10 @@ if ($action eq 'update') { SET version=" . SqlQuote($version) . ", delta_ts = delta_ts WHERE version=" . SqlQuote($versionold) . " - AND product=" . SqlQuote($product)); + AND product_id = $product_id"); SendSQL("UPDATE versions SET value=" . SqlQuote($version) . " - WHERE program=" . SqlQuote($product) . " + WHERE product_id = $product_id AND value=" . SqlQuote($versionold)); unlink "data/versioncache"; print "Updated version.
\n"; diff --git a/enter_bug.cgi b/enter_bug.cgi index 726179722..83f1126f9 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -239,7 +239,9 @@ if (lsearch(\@::enterable_products, $product) == -1) { DisplayError("'" . html_quote($product) . "' is not a valid product."); exit; } - + +my $product_id = get_product_id($product); + if (0 == @{$::components{$product}}) { my $error = "Sorry; there needs to be at least one component for this " . "product in order to create a new bug. "; @@ -261,8 +263,8 @@ elsif (1 == @{$::components{$product}}) { } my @components; -SendSQL("SELECT value, description FROM components " . - "WHERE program = " . SqlQuote($product) . " ORDER BY value"); +SendSQL("SELECT name, description FROM components " . + "WHERE product_id = $product_id ORDER BY name"); while (MoreSQLData()) { my ($name, $description) = FetchSQLData(); @@ -315,7 +317,7 @@ if (exists $::COOKIE{"VERSION-$product"} && my @status = "NEW"; if (UserInGroup("editbugs") || UserInGroup("canconfirm")) { - SendSQL("SELECT votestoconfirm FROM products WHERE product = " . + SendSQL("SELECT votestoconfirm FROM products WHERE name = " . SqlQuote($product)); push(@status, $unconfirmedstate) if (FetchOneColumn()); } diff --git a/globals.pl b/globals.pl index 16cdba73b..3e119644b 100644 --- a/globals.pl +++ b/globals.pl @@ -434,7 +434,10 @@ sub GenerateArrayCode { sub GenerateVersionTable { - SendSQL("select value, program from versions order by value"); + SendSQL("SELECT versions.value, products.name " . + "FROM versions, products " . + "WHERE products.id = versions.product_id " . + "ORDER BY versions.value"); my @line; my %varray; my %carray; @@ -446,7 +449,10 @@ sub GenerateVersionTable { push @{$::versions{$p1}}, $v; $varray{$v} = 1; } - SendSQL("select value, program from components order by value"); + SendSQL("SELECT components.name, products.name " . + "FROM components, products " . + "WHERE products.id = components.product_id " . + "ORDER BY components.name"); while (@line = FetchSQLData()) { my ($c,$p) = (@line); if (!defined $::components{$p}) { @@ -464,7 +470,7 @@ sub GenerateVersionTable { # about them anyway. my $mpart = $dotargetmilestone ? ", milestoneurl" : ""; - SendSQL("select product, description, votesperuser, disallownew$mpart from products ORDER BY product"); + SendSQL("select name, description, votesperuser, disallownew$mpart from products ORDER BY name"); while (@line = FetchSQLData()) { my ($p, $d, $votesperuser, $dis, $u) = (@line); $::proddesc{$p} = $d; @@ -546,7 +552,10 @@ sub GenerateVersionTable { if ($dotargetmilestone) { # reading target milestones in from the database - matthew@zeroknowledge.com - SendSQL("SELECT value, product FROM milestones ORDER BY sortkey, value"); + SendSQL("SELECT milestones.value, products.name " . + "FROM milestones, products " . + "WHERE products.id = milestones.product_id " . + "ORDER BY milestones.sortkey, milestones.value"); my @line; my %tmarray; @::legal_target_milestone = (); @@ -943,6 +952,49 @@ sub DBNameToIdAndCheck { registered for a Bugzilla account."); } +sub get_product_id { + my ($prod) = @_; + PushGlobalSQLState(); + SendSQL("SELECT id FROM products WHERE name = " . SqlQuote($prod)); + my ($prod_id) = FetchSQLData(); + PopGlobalSQLState(); + return $prod_id; +} + +sub get_product_name { + my ($prod_id) = @_; + die "non-numeric prod_id '$prod_id' passed to get_product_name" + unless ($prod_id =~ /^\d+$/); + PushGlobalSQLState(); + SendSQL("SELECT name FROM products WHERE id = $prod_id"); + my ($prod) = FetchSQLData(); + PopGlobalSQLState(); + return $prod; +} + +sub get_component_id { + my ($prod_id, $comp) = @_; + die "non-numeric prod_id '$prod_id' passed to get_component_id" + unless ($prod_id =~ /^\d+$/); + PushGlobalSQLState(); + SendSQL("SELECT id FROM components " . + "WHERE product_id = $prod_id AND name = " . SqlQuote($comp)); + my ($comp_id) = FetchSQLData(); + PopGlobalSQLState(); + return $comp_id; +} + +sub get_component_name { + my ($comp_id) = @_; + die "non-numeric comp_id '$comp_id' passed to get_component_name" + unless ($comp_id =~ /^\d+$/); + PushGlobalSQLState(); + SendSQL("SELECT name FROM components WHERE id = $comp_id"); + my ($comp) = FetchSQLData(); + PopGlobalSQLState(); + return $comp; +} + # Use trick_taint() when you know that there is no way that the data # in a scalar can be tainted, but taint mode still bails on it. # WARNING!! Using this routine on data that really could be tainted @@ -1330,7 +1382,7 @@ sub RemoveVotes { "FROM profiles " . "LEFT JOIN votes ON profiles.userid = votes.who " . "LEFT JOIN bugs USING(bug_id) " . - "LEFT JOIN products USING(product)" . + "LEFT JOIN products ON products.id = bugs.product_id " . "WHERE votes.bug_id = $id " . $whopart); my @list; diff --git a/importxml.pl b/importxml.pl index 964d29a6f..cffeca68c 100755 --- a/importxml.pl +++ b/importxml.pl @@ -570,9 +570,10 @@ for (my $k=1 ; $k <= $bugqty ; $k++) { push (@values, SqlQuote($qa_contact)); push (@query, "qa_contact"); } else { - SendSQL("select initialqacontact from components where program=" . - SqlQuote($product[0]) . - " and value=" . SqlQuote($component[0]) ); + SendSQL("SELECT initialqacontact FROM components, products " + "WHERE components.product_id = products.id" . + " AND products.name = " . SqlQuote($product[0]) . + " AND components.name = " . SqlQuote($component[0]) ); $qa_contact = FetchOneColumn(); push (@values, SqlQuote(DBname_to_id($qa_contact)) ); push (@query, "qa_contact"); diff --git a/long_list.cgi b/long_list.cgi index 33975f5b1..933e90f15 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -41,7 +41,7 @@ GetVersionTable(); my $generic_query = " SELECT bugs.bug_id, - bugs.product, + products.name, bugs.version, bugs.rep_platform, bugs.op_sys, @@ -49,7 +49,7 @@ my $generic_query = " bugs.resolution, bugs.priority, bugs.bug_severity, - bugs.component, + components.name, assign.login_name, report.login_name, bugs.bug_file_loc, @@ -58,8 +58,9 @@ my $generic_query = " bugs.qa_contact, bugs.status_whiteboard, bugs.keywords - FROM bugs,profiles assign,profiles report - WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter"; + FROM bugs,profiles assign,profiles report, products, components + WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter + AND bugs.product_id=products.id AND bugs.component_id=components.id"; my $buglist = $::FORM{'buglist'} || $::FORM{'bug_id'} || diff --git a/post_bug.cgi b/post_bug.cgi index 2edb1aca4..2b4859ccd 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -70,6 +70,11 @@ $template->process("$template_name.txt.tmpl", $vars, \$comment) ValidateComment($comment); my $product = $::FORM{'product'}; +my $product_id = get_product_id($product); +if (!$product_id) { + ThrowUserError("Sorry, the product " . html_quote($product) . + " does not exist"); +} # Set cookies my $cookiepath = Param("cookiepath"); @@ -100,10 +105,11 @@ if(Param("usebuggroupsentry") && GroupExists($product)) { } } -if (!$::FORM{'component'}) { +my $component_id = get_component_id($product_id, $::FORM{component}); +if (!$component_id) { DisplayError("You must choose a component that corresponds to this bug. If necessary, just guess."); - exit; + exit; } if (!defined $::FORM{'short_desc'} || trim($::FORM{'short_desc'}) eq "") { @@ -121,20 +127,20 @@ my $sql_component = SqlQuote($::FORM{'component'}); # Default assignee is the component owner. if ($::FORM{'assigned_to'} eq "") { SendSQL("SELECT initialowner FROM components " . - "WHERE program=$sql_product AND value=$sql_component"); + "WHERE id = $component_id"); $::FORM{'assigned_to'} = FetchOneColumn(); } else { $::FORM{'assigned_to'} = DBNameToIdAndCheck(trim($::FORM{'assigned_to'})); } -my @bug_fields = ("product", "version", "rep_platform", +my @bug_fields = ("version", "rep_platform", "bug_severity", "priority", "op_sys", "assigned_to", - "bug_status", "bug_file_loc", "short_desc", "component", + "bug_status", "bug_file_loc", "short_desc", "target_milestone"); if (Param("useqacontact")) { SendSQL("SELECT initialqacontact FROM components " . - "WHERE program=$sql_product AND value=$sql_component"); + "WHERE id = $component_id"); my $qa_contact = FetchOneColumn(); if (defined $qa_contact && $qa_contact != 0) { $::FORM{'qa_contact'} = $qa_contact; @@ -155,14 +161,14 @@ if (exists $::FORM{'bug_status'}) { if (!exists $::FORM{'bug_status'}) { $::FORM{'bug_status'} = $::unconfirmedstate; - SendSQL("SELECT votestoconfirm FROM products WHERE product=$sql_product"); + SendSQL("SELECT votestoconfirm FROM products WHERE id = $product_id"); if (!FetchOneColumn()) { $::FORM{'bug_status'} = "NEW"; } } if (!exists $::FORM{'target_milestone'}) { - SendSQL("SELECT defaultmilestone FROM products WHERE product=$sql_product"); + SendSQL("SELECT defaultmilestone FROM products WHERE name=$sql_product"); $::FORM{'target_milestone'} = FetchOneColumn(); } @@ -200,6 +206,11 @@ if (exists $::FORM{'bug_status'} $::FORM{'everconfirmed'} = 1; } +$::FORM{'product_id'} = $product_id; +push(@used_fields, "product_id"); +$::FORM{component_id} = $component_id; +push(@used_fields, "component_id"); + my %ccids; my @cc; diff --git a/process_bug.cgi b/process_bug.cgi index d13f51b6c..0a1ff2728 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -174,7 +174,8 @@ sub CheckonComment( $ ) { # and make the user verify the version, component, target milestone, # and bug groups if so. if ( $::FORM{'id'} ) { - SendSQL("SELECT product FROM bugs WHERE bug_id = $::FORM{'id'}"); + SendSQL("SELECT name FROM products, bugs " . + "WHERE products.id = bugs.product_id AND bug_id = $::FORM{'id'}"); $::oldproduct = FetchSQLData(); } if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct) @@ -503,8 +504,8 @@ if($::usergroupset ne '0') { } foreach my $field ("rep_platform", "priority", "bug_severity", - "summary", "component", "bug_file_loc", "short_desc", - "product", "version", "op_sys", + "summary", "bug_file_loc", "short_desc", + "version", "op_sys", "target_milestone", "status_whiteboard") { if (defined $::FORM{$field}) { if ($::FORM{$field} ne $::dontchange) { @@ -514,6 +515,41 @@ foreach my $field ("rep_platform", "priority", "bug_severity", } } +my $prod_id; # Remember, can't use this for mass changes +if ($::FORM{'product'} ne $::dontchange) { + $prod_id = get_product_id($::FORM{'product'}); + if (! $prod_id) { + DisplayError("The " . html_quote($::FORM{'product'}) . + " product doesn't exist."); + exit; + } + DoComma(); + $::query .= "product_id = $prod_id"; +} else { + SendSQL("SELECT DISTINCT product_id FROM bugs WHERE bug_id IN (" . + join(',', @idlist) . ") LIMIT 2"); + $prod_id = FetchOneColumn(); + $prod_id = undef if (FetchOneColumn()); +} + +my $comp_id; # Remember, can't use this for mass changes +if ($::FORM{'component'} ne $::dontchange) { + if (!defined $prod_id) { + ThrowUserError("You cannot change the component from a list of bugs " . + "covering more than one product"); + } + $comp_id = get_component_id($prod_id, + $::FORM{'component'}); + if (! $comp_id) { + DisplayError("The " . html_quote($::FORM{'component'}) . + " component doesn't exist in the " . + html_quote($::FORM{'product'}) . " product"); + exit; + } + DoComma(); + $::query .= "component_id = $comp_id"; +} + # If this installation uses bug aliases, and the user is changing the alias, # add this change to the query. if (Param("usebugaliases") && defined($::FORM{'alias'})) { @@ -708,17 +744,15 @@ SWITCH: for ($::FORM{'knob'}) { DoConfirm(); } ChangeStatus('NEW'); - SendSQL("select initialowner from components where program=" . - SqlQuote($::FORM{'product'}) . " and value=" . - SqlQuote($::FORM{'component'})); + SendSQL("SELECT initialowner FROM components " . + "WHERE components.id = $comp_id"); my $newid = FetchOneColumn(); my $newname = DBID_to_name($newid); DoComma(); $::query .= "assigned_to = $newid"; if (Param("useqacontact")) { - SendSQL("select initialqacontact from components where program=" . - SqlQuote($::FORM{'product'}) . - " and value=" . SqlQuote($::FORM{'component'})); + SendSQL("SELECT initialqacontact FROM components " . + "WHERE components.id = $comp_id"); my $qacontact = FetchOneColumn(); if (defined $qacontact && $qacontact != 0) { DoComma(); @@ -923,8 +957,9 @@ foreach my $id (@idlist) { SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " . "cc AS selectVisible_cc $write, " . "profiles $write, dependencies $write, votes $write, " . + "products READ, components READ, " . "keywords $write, longdescs $write, fielddefs $write, " . - "keyworddefs READ, groups READ, attachments READ, products READ"); + "keyworddefs READ, groups READ, attachments READ"); my @oldvalues = SnapShotBug($id); my %oldhash; my $i = 0; @@ -1270,6 +1305,19 @@ foreach my $id (@idlist) { } if ($old ne $new) { + # Products and components are now stored in the DB using ID's + # We need to translate this to English before logging it + if ($col eq 'product_id') { + $old = get_product_name($old); + $new = get_product_name($new); + $col = 'product'; + } + if ($col eq 'component_id') { + $old = get_component_name($old); + $new = get_component_name($new); + $col = 'component'; + } + # save off the old value for passing to processmail so the old # owner can be notified # diff --git a/queryhelp.cgi b/queryhelp.cgi index 89db7b5cb..16acf73f1 100755 --- a/queryhelp.cgi +++ b/queryhelp.cgi @@ -660,7 +660,7 @@ print qq{ }; -SendSQL("SELECT product,description FROM products ORDER BY product"); +SendSQL("SELECT name, description FROM products ORDER BY name"); while (MoreSQLData()) { my ($product, $productdesc) = FetchSQLData(); @@ -725,7 +725,11 @@ components and their associated products: foreach $product (@products) { - SendSQL("SELECT value,description FROM components WHERE program=" . SqlQuote($product) . " ORDER BY value"); + SendSQL("SELECT components.name, components.description " . + "FROM components, products " . + "WHERE components.product_id = products.id" . + " AND products.name = " . SqlQuote($product) . + "ORDER BY name"); while (MoreSQLData()) { diff --git a/reports.cgi b/reports.cgi index e04a9fd6d..ac77c89fd 100755 --- a/reports.cgi +++ b/reports.cgi @@ -263,6 +263,7 @@ $when

FIN # Build up $query string + my $prod_table = ($FORM{'product'} ne "-All-") ? ", products" : ""; my $query; $query = <\n

"; if( $FORM{'product'} ne "-All-" ) { - SendSQL("SELECT defaultmilestone FROM products WHERE product = " . - SqlQuote($FORM{'product'})); + SendSQL("SELECT defaultmilestone FROM products WHERE id = $product_id"); $ms = FetchOneColumn(); print "Most Doomed for $ms ($FORM{'product'})"; } else { @@ -661,7 +663,7 @@ sub most_doomed_for_milestone { my $query; $query = "select distinct assigned_to from bugs where target_milestone=\"$ms\""; if ($FORM{'product'} ne "-All-" ) { - $query .= "and bugs.product=".SqlQuote($FORM{'product'}); + $query .= "and bugs.product_id=$product_id "; } $query .= <bug list)}); + Alert(qq{Bug(s) found with invalid product/component ID: $product_id/$component_id}); } } @@ -601,7 +599,7 @@ Status("Checking votes/everconfirmed"); @badbugs = (); SendSQL("SELECT bug_id FROM bugs, products " . - "WHERE bugs.product = products.product " . + "WHERE bugs.product_id = products.id " . "AND bug_status = " . SqlQuote($::unconfirmedstate) . ' ' . "AND votestoconfirm <= votes " . "ORDER BY bug_id"); diff --git a/votes.cgi b/votes.cgi index 7a387e0e1..513add4c3 100755 --- a/votes.cgi +++ b/votes.cgi @@ -153,7 +153,7 @@ sub show_user { # we can do it all in one query. my %maxvotesperbug; if($canedit) { - SendSQL("SELECT products.product, products.maxvotesperbug + SendSQL("SELECT products.name, products.maxvotesperbug FROM products"); while (MoreSQLData()) { my ($prod, $max) = FetchSQLData(); @@ -173,10 +173,11 @@ sub show_user { SendSQL("SELECT votes.bug_id, votes.count, bugs.short_desc, bugs.bug_status - FROM votes, bugs + FROM votes, bugs, products WHERE votes.who = $who AND votes.bug_id = bugs.bug_id - AND bugs.product = " . SqlQuote($product) . + AND bugs.product_id = products.id + AND products.name = " . SqlQuote($product) . "ORDER BY votes.bug_id"); while (MoreSQLData()) { @@ -270,9 +271,9 @@ sub record_votes { # If the user is voting for bugs, make sure they aren't overstuffing # the ballot box. if (scalar(@buglist)) { - SendSQL("SELECT bugs.bug_id, bugs.product, products.maxvotesperbug + SendSQL("SELECT bugs.bug_id, products.name, products.maxvotesperbug FROM bugs, products - WHERE products.product = bugs.product + WHERE products.id = bugs.product_id AND bugs.bug_id IN (" . join(", ", @buglist) . ")"); my %prodcount; -- cgit v1.2.3-65-gdbad From dcb07b50e043893ff849b16890f9ea3c6aadc647 Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Wed, 14 Aug 2002 20:42:28 +0000 Subject: Bug 153578 - Attachment modified date is meant to be attachment creation date r=preed, joel --- attachment.cgi | 12 ++----- checksetup.pl | 47 +++++++++++++++++++++++++-- template/en/default/attachment/list.html.tmpl | 2 +- 3 files changed, 49 insertions(+), 12 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 8a6c1b033..5614549e4 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -382,11 +382,6 @@ sub viewall ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = FetchSQLData(); - # Format the attachment's creation/modification date into something readable. - if ($a{'date'} =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { - $a{'date'} = "$3/$4/$2 $5:$6"; - } - # Flag attachments as to whether or not they can be viewed (as opposed to # being downloaded). Currently I decide they are viewable if their MIME type # is either text/*, image/*, or application/vnd.mozilla.*. @@ -480,8 +475,8 @@ sub insert my $thedata = SqlQuote($::FORM{'data'}); # Insert the attachment into the database. - SendSQL("INSERT INTO attachments (bug_id, filename, description, mimetype, ispatch, submitter_id, thedata) - VALUES ($::FORM{'bugid'}, $filename, $description, $contenttype, $::FORM{'ispatch'}, $::userid, $thedata)"); + SendSQL("INSERT INTO attachments (bug_id, creation_ts, filename, description, mimetype, ispatch, submitter_id, thedata) + VALUES ($::FORM{'bugid'}, now(), $filename, $description, $contenttype, $::FORM{'ispatch'}, $::userid, $thedata)"); # Retrieve the ID of the newly created attachment record. SendSQL("SELECT LAST_INSERT_ID()"); @@ -676,8 +671,7 @@ sub update SET description = $quoteddescription , mimetype = $quotedcontenttype , ispatch = $::FORM{'ispatch'} , - isobsolete = $::FORM{'isobsolete'} , - creation_ts = creation_ts + isobsolete = $::FORM{'isobsolete'} WHERE attach_id = $::FORM{'id'} "); diff --git a/checksetup.pl b/checksetup.pl index 9c1150080..7c6b7d399 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1310,7 +1310,7 @@ $table{bugs_activity} = $table{attachments} = 'attach_id mediumint not null auto_increment primary key, bug_id mediumint not null, - creation_ts timestamp, + creation_ts datetime not null, description mediumtext not null, mimetype mediumtext not null, ispatch tinyint, @@ -3159,7 +3159,50 @@ if (GetFieldDef("products", "product")) { $dbh->do("ALTER TABLE components ADD UNIQUE (product_id, name)"); $dbh->do("ALTER TABLE components ADD INDEX (name)"); } - + +# 2002-08-XX - bbaetz@student.usyd.edu.au - bug 153578 +# attachments creation time needs to be a datetime, not a timestamp +my $fielddef; +if (($fielddef = GetFieldDef("attachments", "creation_ts")) && + $fielddef->[1] =~ /^timestamp/) { + print "Fixing creation time on attachments...\n"; + + my $sth = $dbh->prepare("SELECT COUNT(attach_id) FROM attachments"); + $sth->execute(); + my ($attach_count) = $sth->fetchrow_array(); + + if ($attach_count > 1000) { + print "This may take a while...\n"; + } + my $i = 0; + + # This isn't just as simple as changing the field type, because + # the creation_ts was previously updated when an attachment was made + # obsolete from the attachment creation screen. So we have to go + # and recreate these times from the comments.. + $sth = $dbh->prepare("SELECT bug_id, attach_id, submitter_id " . + "FROM attachments"); + $sth->execute(); + + # Restrict this as much as possible in order to avoid false positives, and + # keep the db search time down + my $sth2 = $dbh->prepare("SELECT bug_when FROM longdescs WHERE bug_id=? AND who=? AND thetext LIKE ? ORDER BY bug_when LIMIT 1"); + while (my ($bug_id, $attach_id, $submitter_id) = $sth->fetchrow_array()) { + $sth2->execute($bug_id, $submitter_id, "Created an attachment (id=$attach_id)%"); + my ($when) = $sth2->fetchrow_array(); + if ($when) { + $dbh->do("UPDATE attachments SET creation_ts='$when' WHERE attach_id=$attach_id"); + } else { + print "Warning - could not determine correct creation time for attachment $attach_id on bug $bug_id\n"; + } + ++$i; + print "Converted $i of $attach_count attachments\n" if !($i % 1000); + } + print "Done - converted $i attachments\n"; + + ChangeFieldType("attachments", "creation_ts", "datetime NOT NULL"); +} + # If you had to change the --TABLE-- definition in any way, then add your # differential change code *** A B O V E *** this comment. # diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index 5bc5217c9..bc25c5721 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -24,7 +24,7 @@

- + -- cgit v1.2.3-65-gdbad From 9c0a26d3a5a35788694f5f284c69b995a3298c86 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Tue, 20 Aug 2002 04:17:18 +0000 Subject: Bug 143286 - Add support for Insiders, Private comments, Private Attachments. Patch by bugreport@peshkin.net; r=gerv. --- Attachment.pm | 5 +- Bugzilla/Attachment.pm | 5 +- Bugzilla/Search.pm | 3 + CGI.pl | 2 +- attachment.cgi | 99 ++++++++++++++++--------- checksetup.pl | 10 ++- defparams.pl | 6 ++ globals.pl | 27 ++++--- process_bug.cgi | 21 +++++- processmail | 23 ++++-- template/en/default/attachment/create.html.tmpl | 17 ++++- template/en/default/attachment/edit.html.tmpl | 3 + template/en/default/attachment/list.html.tmpl | 6 +- template/en/default/bug/comments.html.tmpl | 32 +++++--- template/en/default/bug/edit.html.tmpl | 7 +- template/en/default/bug/show-multiple.html.tmpl | 2 +- 16 files changed, 194 insertions(+), 74 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 23e634276..b4216d4c6 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -49,7 +49,7 @@ sub query # of hashes in which each hash represents a single attachment. &::SendSQL(" SELECT attach_id, creation_ts, mimetype, description, ispatch, - isobsolete, submitter_id + isobsolete, isprivate, submitter_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); @@ -57,7 +57,8 @@ sub query my %a; my $submitter_id; ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, - $a{'ispatch'}, $a{'isobsolete'}, $submitter_id) = &::FetchSQLData(); + $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id) + = &::FetchSQLData(); # Format the attachment's creation/modification date into a standard # format (YYYY-MM-DD HH:MM) diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 23e634276..b4216d4c6 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -49,7 +49,7 @@ sub query # of hashes in which each hash represents a single attachment. &::SendSQL(" SELECT attach_id, creation_ts, mimetype, description, ispatch, - isobsolete, submitter_id + isobsolete, isprivate, submitter_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); @@ -57,7 +57,8 @@ sub query my %a; my $submitter_id; ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, - $a{'ispatch'}, $a{'isobsolete'}, $submitter_id) = &::FetchSQLData(); + $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id) + = &::FetchSQLData(); # Format the attachment's creation/modification date into a standard # format (YYYY-MM-DD HH:MM) diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 257b7656d..9ce9d78f4 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -312,6 +312,9 @@ sub init { "^long_?desc," => sub { my $table = "longdescs_$chartid"; push(@supptables, "longdescs $table"); + if (Param("insidergroup") && !UserInGroup(Param("insidergroup"))) { + push(@wherepart, "$table.isprivate < 1") ; + } push(@wherepart, "$table.bug_id = bugs.bug_id"); $f = "$table.thetext"; }, diff --git a/CGI.pl b/CGI.pl index 2a10a335a..76b88d452 100644 --- a/CGI.pl +++ b/CGI.pl @@ -937,7 +937,7 @@ sub CheckIfVotedConfirmed { } AppendComment($id, DBID_to_name($who), - "*** This bug has been confirmed by popular vote. ***"); + "*** This bug has been confirmed by popular vote. ***", 0); $vars->{'type'} = "votes"; $vars->{'id'} = $id; diff --git a/attachment.cgi b/attachment.cgi index 5614549e4..9f3b39fcc 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -109,6 +109,7 @@ elsif ($action eq "update") validateIsPatch(); validateContentType() unless $::FORM{'ispatch'}; validateIsObsolete(); + validatePrivate(); validateStatuses(); update(); } @@ -125,22 +126,25 @@ exit; sub validateID { - # Validate the value of the "id" form field, which must contain an - # integer that is the ID of an existing attachment. + # Validate the value of the "id" form field, which must contain an + # integer that is the ID of an existing attachment. - detaint_natural($::FORM{'id'}) - || DisplayError("You did not enter a valid attachment number.") + detaint_natural($::FORM{'id'}) + || DisplayError("You did not enter a valid attachment number.") && exit; - # Make sure the attachment exists in the database. - SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); - MoreSQLData() - || DisplayError("Attachment #$::FORM{'id'} does not exist.") - && exit; + # Make sure the attachment exists in the database. + SendSQL("SELECT bug_id, isprivate FROM attachments WHERE attach_id = $::FORM{'id'}"); + MoreSQLData() + || DisplayError("Attachment #$::FORM{'id'} does not exist.") + && exit; - # Make sure the user is authorized to access this attachment's bug. - my ($bugid) = FetchSQLData(); - ValidateBugID($bugid); + # Make sure the user is authorized to access this attachment's bug. + my ($bugid, $isprivate) = FetchSQLData(); + ValidateBugID($bugid); + if (($isprivate > 0 ) && Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { + ThrowUserError("You are not permitted access to this attachment."); + } } sub validateCanEdit @@ -244,6 +248,14 @@ sub validateIsObsolete $::FORM{'isobsolete'} = $::FORM{'isobsolete'} ? 1 : 0; } +sub validatePrivate +{ + # Set the isprivate flag to zero if it is undefined, since the UI uses + # an HTML checkbox to represent this flag, and unchecked HTML checkboxes + # do not get sent in HTML requests. + $::FORM{'isprivate'} = $::FORM{'isprivate'} ? 1 : 0; +} + sub validateStatuses { # Get a list of attachment statuses that are valid for this attachment. @@ -354,16 +366,16 @@ sub validateObsolete sub view { - # Display an attachment. + # Display an attachment. - # Retrieve the attachment content and its content type from the database. - SendSQL("SELECT mimetype, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($contenttype, $thedata) = FetchSQLData(); + # Retrieve the attachment content and its content type from the database. + SendSQL("SELECT mimetype, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); + my ($contenttype, $thedata) = FetchSQLData(); - # Return the appropriate HTTP response headers. - print "Content-Type: $contenttype\n\n"; + # Return the appropriate HTTP response headers. + print "Content-Type: $contenttype\n\n"; - print $thedata; + print $thedata; } @@ -373,14 +385,20 @@ sub viewall # Retrieve the attachments from the database and write them into an array # of hashes where each hash represents one attachment. - SendSQL("SELECT attach_id, creation_ts, mimetype, description, ispatch, isobsolete - FROM attachments WHERE bug_id = $::FORM{'bugid'} ORDER BY attach_id"); + my $privacy = ""; + if (Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { + $privacy = "AND isprivate < 1 "; + } + SendSQL("SELECT attach_id, creation_ts, mimetype, description, + ispatch, isobsolete, isprivate + FROM attachments WHERE bug_id = $::FORM{'bugid'} $privacy + ORDER BY attach_id"); my @attachments; # the attachments array while (MoreSQLData()) { my %a; # the attachment hash ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, - $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}) = FetchSQLData(); + $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}) = FetchSQLData(); # Flag attachments as to whether or not they can be viewed (as opposed to # being downloaded). Currently I decide they are viewable if their MIME type @@ -432,7 +450,7 @@ sub enter if (!UserInGroup("editbugs")) { $canEdit = "AND submitter_id = $::userid"; } - SendSQL("SELECT attach_id, description + SendSQL("SELECT attach_id, description, isprivate FROM attachments WHERE bug_id = $::FORM{'bugid'} AND isobsolete = 0 $canEdit @@ -440,7 +458,7 @@ sub enter my @attachments; # the attachments array while ( MoreSQLData() ) { my %a; # the attachment hash - ($a{'id'}, $a{'description'}) = FetchSQLData(); + ($a{'id'}, $a{'description'}, $a{'isprivate'}) = FetchSQLData(); # Add the hash representing the attachment to the array of attachments. push @attachments, \%a; @@ -473,10 +491,11 @@ sub insert my $description = SqlQuote($::FORM{'description'}); my $contenttype = SqlQuote($::FORM{'contenttype'}); my $thedata = SqlQuote($::FORM{'data'}); + my $isprivate = $::FORM{'isprivate'} ? 1 : 0; # Insert the attachment into the database. - SendSQL("INSERT INTO attachments (bug_id, creation_ts, filename, description, mimetype, ispatch, submitter_id, thedata) - VALUES ($::FORM{'bugid'}, now(), $filename, $description, $contenttype, $::FORM{'ispatch'}, $::userid, $thedata)"); + SendSQL("INSERT INTO attachments (bug_id, creation_ts, filename, description, mimetype, ispatch, isprivate, submitter_id, thedata) + VALUES ($::FORM{'bugid'}, now(), $filename, $description, $contenttype, $::FORM{'ispatch'}, $isprivate, $::userid, $thedata)"); # Retrieve the ID of the newly created attachment record. SendSQL("SELECT LAST_INSERT_ID()"); @@ -493,14 +512,15 @@ sub insert AppendComment($::FORM{'bugid'}, $::COOKIE{"Bugzilla_login"}, - $comment); + $comment, + $isprivate); # Make existing attachments obsolete. my $fieldid = GetFieldID('attachments.isobsolete'); foreach my $attachid (@{$::MFORM{'obsolete'}}) { - SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $attachid"); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($::FORM{'bugid'}, $attachid, $::userid, NOW(), $fieldid, '0', '1')"); + SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $attachid"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($::FORM{'bugid'}, $attachid, $::userid, NOW(), $fieldid, '0', '1')"); } # Send mail to let people know the attachment has been created. Uses a @@ -540,9 +560,9 @@ sub edit # Users cannot edit the content of the attachment itself. # Retrieve the attachment from the database. - SendSQL("SELECT description, mimetype, bug_id, ispatch, isobsolete + SendSQL("SELECT description, mimetype, bug_id, ispatch, isobsolete, isprivate FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($description, $contenttype, $bugid, $ispatch, $isobsolete) = FetchSQLData(); + my ($description, $contenttype, $bugid, $ispatch, $isobsolete, $isprivate) = FetchSQLData(); # Flag attachment as to whether or not it can be viewed (as opposed to # being downloaded). Currently I decide it is viewable if its content @@ -592,6 +612,7 @@ sub edit $vars->{'bugsummary'} = $bugsummary; $vars->{'ispatch'} = $ispatch; $vars->{'isobsolete'} = $isobsolete; + $vars->{'isprivate'} = $isprivate; $vars->{'isviewable'} = $isviewable; $vars->{'statuses'} = \%statuses; $vars->{'statusdefs'} = \@statusdefs; @@ -619,12 +640,12 @@ sub update # Lock database tables in preparation for updating the attachment. SendSQL("LOCK TABLES attachments WRITE , attachstatuses WRITE , attachstatusdefs READ , fielddefs READ , bugs_activity WRITE"); - # Get a copy of the attachment record before we make changes # so we can record those changes in the activity table. - SendSQL("SELECT description, mimetype, ispatch, isobsolete + SendSQL("SELECT description, mimetype, ispatch, isobsolete, isprivate FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($olddescription, $oldcontenttype, $oldispatch, $oldisobsolete) = FetchSQLData(); + my ($olddescription, $oldcontenttype, $oldispatch, $oldisobsolete, + $oldisprivate ) = FetchSQLData(); # Get the list of old status flags. SendSQL("SELECT attachstatusdefs.name @@ -672,6 +693,7 @@ sub update mimetype = $quotedcontenttype , ispatch = $::FORM{'ispatch'} , isobsolete = $::FORM{'isobsolete'} + isprivate = $::FORM{'isprivate'} , WHERE attach_id = $::FORM{'id'} "); @@ -698,6 +720,11 @@ sub update SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldisobsolete, $::FORM{'isobsolete'})"); } + if ($oldisprivate ne $::FORM{'isprivate'}) { + my $fieldid = GetFieldID('attachments.isprivate'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldisprivate, $::FORM{'isprivate'})"); + } if ($oldstatuslist ne $newstatuslist) { my ($removed, $added) = DiffStrings($oldstatuslist, $newstatuslist); my $quotedremoved = SqlQuote($removed); @@ -759,7 +786,7 @@ sub update my $neverused = $::userid; # Append the comment to the list of comments in the database. - AppendComment($bugid, $who, $wrappedcomment); + AppendComment($bugid, $who, $wrappedcomment, $::FORM{'isprivate'}); } diff --git a/checksetup.pl b/checksetup.pl index 34b487443..0a37174dd 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1316,6 +1316,7 @@ $table{attachments} = thedata longblob not null, submitter_id mediumint not null, isobsolete tinyint not null default 0, + isprivate tinyint not null default 0, index(bug_id), index(creation_ts)'; @@ -1414,7 +1415,7 @@ $table{longdescs} = who mediumint not null, bug_when datetime not null, thetext mediumtext, - + isprivate tinyint not null default 0, index(bug_id), index(who), index(bug_when)'; @@ -1794,6 +1795,7 @@ AddFDef("attachments.thedata", "Attachment data", 0); AddFDef("attachments.mimetype", "Attachment mime type", 0); AddFDef("attachments.ispatch", "Attachment is patch", 0); AddFDef("attachments.isobsolete", "Attachment is obsolete", 0); +AddFDef("attachments.isprivate", "Attachment is private", 0); AddFDef("attachstatusdefs.name", "Attachment Status", 0); AddFDef("target_milestone", "Target Milestone", 0); AddFDef("delta_ts", "Last changed date", 0); @@ -2503,6 +2505,12 @@ if (GetFieldDef('bugs_activity', 'field')) { DropField('bugs_activity', 'field'); } + +# 2002-05-10 - enhanchment bug 143826 +# Add private comments and private attachments on less-private bugs +AddField('longdescs', 'isprivate', 'tinyint not null default 0'); +AddField('attachments', 'isprivate', 'tinyint not null default 0'); + # 2000-01-18 New email-notification scheme uses a new field in the bug to diff --git a/defparams.pl b/defparams.pl index bc73de600..4f30f85a9 100644 --- a/defparams.pl +++ b/defparams.pl @@ -717,4 +717,10 @@ DefParam("maxattachmentsize" , "t" , '1000'); +DefParam("insidergroup", + "The name of the group of users who can see/change private comments + and attachments.", + "t", + ''); 1; + diff --git a/globals.pl b/globals.pl index 9f15976b2..b437d343f 100644 --- a/globals.pl +++ b/globals.pl @@ -323,7 +323,7 @@ sub FetchOneColumn { "status", "resolution", "summary"); sub AppendComment { - my ($bugid,$who,$comment) = (@_); + my ($bugid,$who,$comment,$isprivate) = (@_); $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. @@ -331,9 +331,10 @@ sub AppendComment { } my $whoid = DBNameToIdAndCheck($who); - - SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext) " . - "VALUES($bugid, $whoid, now(), " . SqlQuote($comment) . ")"); + my $privacyval = $isprivate ? 1 : 0 ; + SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext, isprivate) " . + "VALUES($bugid, $whoid, now(), " . SqlQuote($comment) . ", " . + $privacyval . ")"); SendSQL("UPDATE bugs SET delta_ts = now() WHERE bug_id = $bugid"); } @@ -1137,8 +1138,9 @@ sub GetLongDescriptionAsText { my ($id, $start, $end) = (@_); my $result = ""; my $count = 0; + my $anyprivate = 0; my ($query) = ("SELECT profiles.login_name, longdescs.bug_when, " . - " longdescs.thetext " . + " longdescs.thetext, longdescs.isprivate " . "FROM longdescs, profiles " . "WHERE profiles.userid = longdescs.who " . "AND longdescs.bug_id = $id "); @@ -1156,25 +1158,29 @@ sub GetLongDescriptionAsText { $query .= "ORDER BY longdescs.bug_when"; SendSQL($query); while (MoreSQLData()) { - my ($who, $when, $text) = (FetchSQLData()); + my ($who, $when, $text, $isprivate) = (FetchSQLData()); if ($count) { $result .= "\n\n------- Additional Comments From $who".Param('emailsuffix')." ". time2str("%Y-%m-%d %H:%M", str2time($when)) . " -------\n"; } + if (($isprivate > 0) && Param("insidergroup")) { + $anyprivate = 1; + } $result .= $text; $count++; } - return $result; + return ($result, $anyprivate); } sub GetComments { my ($id) = (@_); my @comments; - SendSQL("SELECT profiles.realname, profiles.login_name, date_format(longdescs.bug_when,'%Y-%m-%d %H:%i'), - longdescs.thetext + longdescs.thetext, + isprivate, + date_format(longdescs.bug_when,'%Y%m%d%H%i%s') FROM longdescs, profiles WHERE profiles.userid = longdescs.who AND longdescs.bug_id = $id @@ -1182,7 +1188,8 @@ sub GetComments { while (MoreSQLData()) { my %comment; - ($comment{'name'}, $comment{'email'}, $comment{'time'}, $comment{'body'}) = FetchSQLData(); + ($comment{'name'}, $comment{'email'}, $comment{'time'}, $comment{'body'}, + $comment{'isprivate'}, $comment{'when'}) = FetchSQLData(); $comment{'email'} .= Param('emailsuffix'); $comment{'name'} = $comment{'name'} || $comment{'email'}; diff --git a/process_bug.cgi b/process_bug.cgi index 85522b66d..02089297a 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -630,6 +630,22 @@ if ( $::FORM{'id'} ) { } } +if ($::FORM{'id'} && + (Param("insidergroup") && UserInGroup(Param("insidergroup")))) { + detaint_natural($::FORM{'id'}); + foreach my $field (keys %::FORM) { + if ($field =~ /when-([0-9]+)/) { + my $sequence = $1; + my $private = $::FORM{"isprivate-$sequence"} ? 1 : 0 ; + if ($private != $::FORM{"oisprivate-$sequence"}) { + detaint_natural($::FORM{"$field"}); + SendSQL("UPDATE longdescs SET isprivate = $private + WHERE bug_id = $::FORM{'id'} AND bug_when = " . $::FORM{"$field"}); + } + } + + } +} my $duplicate = 0; @@ -1098,7 +1114,8 @@ foreach my $id (@idlist) { $timestamp = FetchOneColumn(); if (defined $::FORM{'comment'}) { - AppendComment($id, $::COOKIE{'Bugzilla_login'}, $::FORM{'comment'}); + AppendComment($id, $::COOKIE{'Bugzilla_login'}, $::FORM{'comment'}, + $::FORM{'commentprivacy'}); } my $removedCcString = ""; @@ -1368,7 +1385,7 @@ foreach my $id (@idlist) { LogActivityEntry($duplicate,"cc","",DBID_to_name($reporter)); SendSQL("INSERT INTO cc (who, bug_id) VALUES ($reporter, " . SqlQuote($duplicate) . ")"); } - AppendComment($duplicate, $::COOKIE{'Bugzilla_login'}, "*** Bug $::FORM{'id'} has been marked as a duplicate of this bug. ***"); + AppendComment($duplicate, $::COOKIE{'Bugzilla_login'}, "*** Bug $::FORM{'id'} has been marked as a duplicate of this bug. ***", 1); CheckFormFieldDefined(\%::FORM,'comment'); SendSQL("INSERT INTO duplicates VALUES ($duplicate, $::FORM{'id'})"); diff --git a/processmail b/processmail index 20833a4f4..8d4dc4b69 100755 --- a/processmail +++ b/processmail @@ -225,7 +225,7 @@ sub ProcessOneBug { } - my $newcomments = GetLongDescriptionAsText($id, $start, $end); + my ($newcomments, $anyprivate) = GetLongDescriptionAsText($id, $start, $end); # # Start of email filtering code @@ -303,7 +303,8 @@ sub ProcessOneBug { \@reasons, \%values, \%defmailhead, \%fielddescription, $difftext, - $newcomments, $start, $id, + $newcomments, $anyprivate, + $start, $id, \@depbugs))) { @@ -612,9 +613,9 @@ sub filterEmailGroup ($$$) { return @recipients; } -sub NewProcessOnePerson ($$$$$$$$$$$$) { +sub NewProcessOnePerson ($$$$$$$$$$$$$) { my ($person, $count, $hlRef, $reasonsRef, $valueRef, $dmhRef, $fdRef, $difftext, - $newcomments, $start, $id, $depbugsRef) = @_; + $newcomments, $anyprivate, $start, $id, $depbugsRef) = @_; my %values = %$valueRef; my @headerlist = @$hlRef; @@ -650,7 +651,19 @@ sub NewProcessOnePerson ($$$$$$$$$$$$) { # quietly disappear from their radar. # return unless CanSeeBug($id, $userid, $groupset); - + + # Drop any non-insiders if the comment is private + if (Param("insidergroup") && ($anyprivate != 0)) { + ConnectToDatabase(); + PushGlobalSQLState(); + SendSQL("select (bit & $groupset ) != 0 from groups where name = " . SqlQuote(Param("insidergroup"))); + my $bit = FetchOneColumn(); + PopGlobalSQLState(); + if (!$bit) { + return; + } + } + # We shouldn't send changedmail if this is a dependency mail, and any of # the depending bugs is not visible to the user. foreach my $dep_id (@depbugs) { diff --git a/template/en/default/attachment/create.html.tmpl b/template/en/default/attachment/create.html.tmpl index a80708ee3..1c00146e5 100644 --- a/template/en/default/attachment/create.html.tmpl +++ b/template/en/default/attachment/create.html.tmpl @@ -94,15 +94,28 @@ onchange="if (this.value) this.form.contenttypemethod[2].checked = true;"> + [% IF (Param("insidergroup") && UserInGroup(Param("insidergroup"))) %] + + + + + [% END %] - + [% canseeprivate = !Param("insidergroup") || UserInGroup(Param("insidergroup")) %] [% FOREACH attachment = attachments %] - + [% IF !attachment.isprivate || canseeprivate %] + + [% END %] [% END %] diff --git a/template/en/default/bug/comments.html.tmpl b/template/en/default/bug/comments.html.tmpl index f1f8e762e..7a8ae73db 100644 --- a/template/en/default/bug/comments.html.tmpl +++ b/template/en/default/bug/comments.html.tmpl @@ -19,8 +19,9 @@ # Contributor(s): Gervase Markham #%] -[% DEFAULT start_at = 0 %] +[% DEFAULT start_at = 0 mode = "show" %] [% count = 0 %] +[% isinsider = Param("insidergroup") && UserInGroup(Param("insidergroup")) %] [% FOREACH comment = comments %] [% IF count >= start_at %] [% PROCESS a_comment %] @@ -35,14 +36,25 @@ [%############################################################################%] [% BLOCK a_comment %] - [% IF count > 0 %] -
- ------- Additional Comment - #[% count %] From - [% comment.name FILTER html %] - [%+ comment.time %] ------- - - [% END %] + [% IF NOT comment.isprivate || isinsider %] +
+ [% IF count > 0 %] +
+ ------- Additional Comment + #[% count %] From + [% comment.name FILTER html %] + [%+ comment.time %] ------- + + [% END %] + [% IF mode == "edit" && isinsider %] + + + + Private + + [% END %] [%# Don't indent the
 block, since then the spaces are displayed in the
   # generated HTML
@@ -50,4 +62,6 @@
 
   [%- quoteUrls(comment.body) -%]
 
+
+ [% END %] [% END %] diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index 502952ef2..9cf33b8b5 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -28,6 +28,7 @@ h2 = filtered_desc h3 = "Last modified: $bug.calc_disp_date" header_html = navigation_links + style_urls = [ "css/edit_bug.css" ] %] [% END %] @@ -310,6 +311,9 @@
Additional Comments: + [% IF Param("insidergroup") && UserInGroup(Param("insidergroup")) %] + Private + [% END %]
@@ -482,7 +486,6 @@ value="[% Param("move-button-text") %]"> [% END %]

- [%# *** Additional Comments *** %] @@ -502,8 +505,10 @@ [% PROCESS bug/comments.html.tmpl comments = bug.comments + mode = "edit" %] +
[% PROCESS bug/navigate.html.tmpl %] diff --git a/template/en/default/bug/show-multiple.html.tmpl b/template/en/default/bug/show-multiple.html.tmpl index 7c3e7407f..0c089e9c5 100644 --- a/template/en/default/bug/show-multiple.html.tmpl +++ b/template/en/default/bug/show-multiple.html.tmpl @@ -22,8 +22,8 @@ [% PROCESS global/header.html.tmpl title = "Full Text Bug Listing" + style_urls = [ "css/show_multiple.css" ] %] - [% IF bugs.first %] [% FOREACH bug = bugs %] [% PROCESS bug_display %] -- cgit v1.2.3-65-gdbad From 942165b87532fd7f63042d5ff2e95df7af5e8400 Mon Sep 17 00:00:00 2001 From: "bugreport%peshkin.net" <> Date: Sat, 24 Aug 2002 21:51:27 +0000 Subject: Fixed merge problem from checkin of 143826 - No bug --- attachment.cgi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 9f3b39fcc..a94ebc835 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -692,8 +692,8 @@ sub update SET description = $quoteddescription , mimetype = $quotedcontenttype , ispatch = $::FORM{'ispatch'} , - isobsolete = $::FORM{'isobsolete'} - isprivate = $::FORM{'isprivate'} , + isobsolete = $::FORM{'isobsolete'} , + isprivate = $::FORM{'isprivate'} WHERE attach_id = $::FORM{'id'} "); -- cgit v1.2.3-65-gdbad From 5b18c51fdfab10bd25a17e9e729aedf4782cd6f7 Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Mon, 26 Aug 2002 13:16:47 +0000 Subject: Bug 76923 - Don't |use diagnostics| (its really expensive at startup time) r=joel x2 --- Attachment.pm | 1 - Bug.pm | 1 - Bugzilla/Attachment.pm | 1 - Bugzilla/Bug.pm | 1 - Bugzilla/RelationSet.pm | 1 - Bugzilla/Search.pm | 1 - Bugzilla/Token.pm | 1 - Bugzilla/Util.pm | 1 - CGI.pl | 1 - RelationSet.pm | 1 - Token.pm | 1 - attachment.cgi | 1 - bug_form.pl | 1 - buglist.cgi | 1 - checksetup.pl | 1 - colchange.cgi | 1 - collectstats.pl | 1 - contrib/BugzillaEmail.pm | 1 - contrib/bug_email.pl | 3 +-- contrib/bugzilla_email_append.pl | 1 - contrib/mysqld-watcher.pl | 1 - createaccount.cgi | 1 - defparams.pl | 1 - describecomponents.cgi | 1 - describekeywords.cgi | 1 - doeditparams.cgi | 1 - duplicates.cgi | 1 - editattachstatuses.cgi | 1 - editcomponents.cgi | 1 - editgroups.cgi | 1 - editkeywords.cgi | 1 - editmilestones.cgi | 1 - editparams.cgi | 1 - editproducts.cgi | 1 - editusers.cgi | 1 - editversions.cgi | 1 - enter_bug.cgi | 1 - globals.pl | 1 - importxml.pl | 1 - index.cgi | 1 - long_list.cgi | 1 - move.pl | 1 - page.cgi | 1 - post_bug.cgi | 1 - process_bug.cgi | 1 - processmail | 1 - query.cgi | 1 - queryhelp.cgi | 1 - quips.cgi | 1 - relogin.cgi | 1 - reports.cgi | 1 - sanitycheck.cgi | 1 - show_activity.cgi | 1 - show_bug.cgi | 1 - showattachment.cgi | 1 - showdependencygraph.cgi | 1 - showdependencytree.cgi | 1 - sidebar.cgi | 1 - syncshadowdb | 1 - t/Support/Templates.pm | 1 - token.cgi | 1 - userprefs.cgi | 1 - votes.cgi | 1 - whineatnews.pl | 1 - xml.cgi | 1 - 65 files changed, 1 insertion(+), 66 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index b4216d4c6..3a6248cf4 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -24,7 +24,6 @@ # Module Initialization ############################################################################ -use diagnostics; use strict; package Attachment; diff --git a/Bug.pm b/Bug.pm index 51b51cec9..7857cb924 100755 --- a/Bug.pm +++ b/Bug.pm @@ -21,7 +21,6 @@ # Terry Weissman # Chris Yeh -use diagnostics; use strict; use DBI; diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index b4216d4c6..3a6248cf4 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -24,7 +24,6 @@ # Module Initialization ############################################################################ -use diagnostics; use strict; package Attachment; diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 51b51cec9..7857cb924 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -21,7 +21,6 @@ # Terry Weissman # Chris Yeh -use diagnostics; use strict; use DBI; diff --git a/Bugzilla/RelationSet.pm b/Bugzilla/RelationSet.pm index 8668519b9..a3af4b60a 100644 --- a/Bugzilla/RelationSet.pm +++ b/Bugzilla/RelationSet.pm @@ -29,7 +29,6 @@ # might involve turning this into a virtual base class, and having # UserSet and KeywordSet types that inherit from it. -use diagnostics; use strict; # Everything that uses RelationSet should already have globals.pl loaded diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 562a68e48..b8cc9fed8 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -25,7 +25,6 @@ # Myk Melez # Michael Schindler -use diagnostics; use strict; require "globals.pl"; diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 78eef9335..6d0741133 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -24,7 +24,6 @@ ################################################################################ # Make it harder for us to do dangerous things in Perl. -use diagnostics; use strict; # Bundle the functions in this file together into the "Token" package. diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index 74e7c1fe6..3fe3a37ea 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -69,7 +69,6 @@ use base qw(Exporter); trim); use strict; -use diagnostics; =head1 FUNCTIONS diff --git a/CGI.pl b/CGI.pl index b2316d555..69819263a 100644 --- a/CGI.pl +++ b/CGI.pl @@ -27,7 +27,6 @@ # Contains some global routines used throughout the CGI scripts of Bugzilla. -use diagnostics; use strict; use lib "."; diff --git a/RelationSet.pm b/RelationSet.pm index 8668519b9..a3af4b60a 100644 --- a/RelationSet.pm +++ b/RelationSet.pm @@ -29,7 +29,6 @@ # might involve turning this into a virtual base class, and having # UserSet and KeywordSet types that inherit from it. -use diagnostics; use strict; # Everything that uses RelationSet should already have globals.pl loaded diff --git a/Token.pm b/Token.pm index 78eef9335..6d0741133 100644 --- a/Token.pm +++ b/Token.pm @@ -24,7 +24,6 @@ ################################################################################ # Make it harder for us to do dangerous things in Perl. -use diagnostics; use strict; # Bundle the functions in this file together into the "Token" package. diff --git a/attachment.cgi b/attachment.cgi index a94ebc835..43b2b4940 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -26,7 +26,6 @@ ################################################################################ # Make it harder for us to do dangerous things in Perl. -use diagnostics; use strict; use lib qw(.); diff --git a/bug_form.pl b/bug_form.pl index c4d623e12..e6b59e051 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -21,7 +21,6 @@ # Dave Miller # Vaskin Kissoyan -use diagnostics; use strict; use RelationSet; diff --git a/buglist.cgi b/buglist.cgi index cf25811da..c5359c6e3 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -29,7 +29,6 @@ ################################################################################ # Make it harder for us to do dangerous things in Perl. -use diagnostics; use strict; use lib qw(.); diff --git a/checksetup.pl b/checksetup.pl index 7ff6abada..8bba2996b 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -94,7 +94,6 @@ # Global definitions ########################################################################### -use diagnostics; use strict; # 12/17/00 justdave@syndicomm.com - removed declarations of the localconfig diff --git a/colchange.cgi b/colchange.cgi index 4b1cfc819..2ef7c7d10 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/collectstats.pl b/collectstats.pl index a47e2174d..8caf92d77 100755 --- a/collectstats.pl +++ b/collectstats.pl @@ -26,7 +26,6 @@ use AnyDBM_File; -use diagnostics; use strict; use vars @::legal_product; diff --git a/contrib/BugzillaEmail.pm b/contrib/BugzillaEmail.pm index aaba0f4e0..48602cdb7 100644 --- a/contrib/BugzillaEmail.pm +++ b/contrib/BugzillaEmail.pm @@ -29,7 +29,6 @@ push @INC, "../."; # this script now lives in contrib require "globals.pl"; -use diagnostics; use strict; my $EMAIL_TRANSFORM_NONE = "email_transform_none"; diff --git a/contrib/bug_email.pl b/contrib/bug_email.pl index 94338dbfb..fb2bbec4d 100755 --- a/contrib/bug_email.pl +++ b/contrib/bug_email.pl @@ -37,7 +37,7 @@ # # You need to work with bug_email.pl the MIME::Parser installed. # -# $Id: bug_email.pl,v 1.12 2002/08/17 14:19:57 bbaetz%student.usyd.edu.au Exp $ +# $Id: bug_email.pl,v 1.13 2002/08/26 06:17:21 bbaetz%student.usyd.edu.au Exp $ ############################################################### # 02/12/2000 (SML) @@ -66,7 +66,6 @@ # - integrate some setup in the checksetup.pl script # - gpg signatures for security -use diagnostics; use strict; use MIME::Parser; diff --git a/contrib/bugzilla_email_append.pl b/contrib/bugzilla_email_append.pl index 826b49198..7e1de847b 100755 --- a/contrib/bugzilla_email_append.pl +++ b/contrib/bugzilla_email_append.pl @@ -28,7 +28,6 @@ # 1. better way to get the body text (I don't know what dump_entity() is # actually doing -use diagnostics; use strict; use MIME::Parser; diff --git a/contrib/mysqld-watcher.pl b/contrib/mysqld-watcher.pl index ccc1f3878..30945a5ff 100755 --- a/contrib/mysqld-watcher.pl +++ b/contrib/mysqld-watcher.pl @@ -23,7 +23,6 @@ # mysqld-watcher.pl - a script that watches the running instance of # mysqld and kills off any long-running SELECTs against the shadow_db # -use diagnostics; use strict; # some configurables: diff --git a/createaccount.cgi b/createaccount.cgi index 8a5b85782..79be1bb64 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -24,7 +24,6 @@ # Christopher Aillon # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/defparams.pl b/defparams.pl index 4f30f85a9..67870893d 100644 --- a/defparams.pl +++ b/defparams.pl @@ -33,7 +33,6 @@ # Only adding new parameters is done here. Once the parameter exists, you # must use %baseurl%/editparams.cgi from the web to edit the settings. -use diagnostics; use strict; # Shut up misguided -w warnings about "used only once". For some reason, diff --git a/describecomponents.cgi b/describecomponents.cgi index edf9349ab..9edb5dfa0 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -27,7 +27,6 @@ use vars qw( $userid ); -use diagnostics; use strict; use lib qw(.); diff --git a/describekeywords.cgi b/describekeywords.cgi index 503e0d5a3..07f08d09b 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # Contributor(s): Gervase Markham -use diagnostics; use strict; use lib "."; diff --git a/doeditparams.cgi b/doeditparams.cgi index c237a1997..b7c834979 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # J. Paul Reed -use diagnostics; use strict; use lib qw(.); diff --git a/duplicates.cgi b/duplicates.cgi index 2d46e0d79..285c65717 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -23,7 +23,6 @@ # Generates mostfreq list from data collected by collectstats.pl. -use diagnostics; use strict; use AnyDBM_File; diff --git a/editattachstatuses.cgi b/editattachstatuses.cgi index ff7822181..eedf2add4 100755 --- a/editattachstatuses.cgi +++ b/editattachstatuses.cgi @@ -26,7 +26,6 @@ ################################################################################ # Make it harder for us to do dangerous things in Perl. -use diagnostics; use strict; use lib "."; diff --git a/editcomponents.cgi b/editcomponents.cgi index 3f1619739..7ad81ddfa 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -25,7 +25,6 @@ # # Holger Schurig -use diagnostics; use strict; use lib "."; diff --git a/editgroups.cgi b/editgroups.cgi index 623cf47d3..cd1805792 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -23,7 +23,6 @@ # Code derived from editowners.cgi and editusers.cgi -use diagnostics; use strict; use lib "."; diff --git a/editkeywords.cgi b/editkeywords.cgi index 1b8d6ef30..ed298ef9b 100755 --- a/editkeywords.cgi +++ b/editkeywords.cgi @@ -20,7 +20,6 @@ # # Contributor(s): Terry Weissman -use diagnostics; use strict; use lib "."; diff --git a/editmilestones.cgi b/editmilestones.cgi index 67d84fcce..c7d598bea 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -13,7 +13,6 @@ # -use diagnostics; use strict; use lib "."; diff --git a/editparams.cgi b/editparams.cgi index 0e1b7161f..ef9e14946 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -22,7 +22,6 @@ # J. Paul Reed -use diagnostics; use strict; use lib "."; diff --git a/editproducts.cgi b/editproducts.cgi index 4b0698b35..520e46d50 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -27,7 +27,6 @@ # # Holger Schurig -use diagnostics; use strict; use lib "."; diff --git a/editusers.cgi b/editusers.cgi index 0e25f0ac3..4586345af 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -27,7 +27,6 @@ # # Holger Schurig -use diagnostics; use strict; use lib "."; diff --git a/editversions.cgi b/editversions.cgi index abeed2570..5f420af43 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -26,7 +26,6 @@ # # Holger Schurig -use diagnostics; use strict; use lib "."; diff --git a/enter_bug.cgi b/enter_bug.cgi index 83f1126f9..7aa6dfc66 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -32,7 +32,6 @@ # ############################################################################## -use diagnostics; use strict; use lib qw(.); diff --git a/globals.pl b/globals.pl index b437d343f..164bad727 100644 --- a/globals.pl +++ b/globals.pl @@ -25,7 +25,6 @@ # Contains some global variables and routines used throughout bugzilla. -use diagnostics; use strict; use Bugzilla::Util; diff --git a/importxml.pl b/importxml.pl index 092170c03..1777bfc4a 100755 --- a/importxml.pl +++ b/importxml.pl @@ -25,7 +25,6 @@ # a new bug into bugzilla. Everything before the beginning # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/move.pl b/move.pl index 66a75f95f..65592b02f 100755 --- a/move.pl +++ b/move.pl @@ -21,7 +21,6 @@ # Contributor(s): Dawn Endico # Terry Weissman -use diagnostics; use strict; use lib qw(.); diff --git a/page.cgi b/page.cgi index fb53f8b47..cc26f93db 100755 --- a/page.cgi +++ b/page.cgi @@ -28,7 +28,6 @@ # either case), numbers 0-9, the underscore "_" and the hyphen "-". ############################################################################### -use diagnostics; use strict; use lib "."; diff --git a/post_bug.cgi b/post_bug.cgi index 55ee27301..bab55a9ac 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -23,7 +23,6 @@ # Joe Robins # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/process_bug.cgi b/process_bug.cgi index 653c365ce..a34616058 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -24,7 +24,6 @@ # Christopher Aillon # Myk Melez -use diagnostics; use strict; my $UserInEditGroupSet = -1; diff --git a/processmail b/processmail index 8d4dc4b69..5a1460c18 100755 --- a/processmail +++ b/processmail @@ -25,7 +25,6 @@ # Jacob Steenhagen # Matthew Tuck -use diagnostics; use strict; use lib "."; diff --git a/query.cgi b/query.cgi index af4b54164..2ce900767 100755 --- a/query.cgi +++ b/query.cgi @@ -23,7 +23,6 @@ # Matthias Radestock # Gervase Markham -use diagnostics; use strict; use lib "."; diff --git a/queryhelp.cgi b/queryhelp.cgi index 16acf73f1..bfd7c0f69 100755 --- a/queryhelp.cgi +++ b/queryhelp.cgi @@ -24,7 +24,6 @@ use vars %::FORM; -use diagnostics; use strict; use lib qw(.); diff --git a/quips.cgi b/quips.cgi index a50bbc54a..7db312c61 100755 --- a/quips.cgi +++ b/quips.cgi @@ -22,7 +22,6 @@ # Gervase Markham # David Fallon -use diagnostics; use strict; use vars qw( diff --git a/relogin.cgi b/relogin.cgi index 3bab9fdc5..493f5c200 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # Gervase Markham -use diagnostics; use strict; use vars %::COOKIE; diff --git a/reports.cgi b/reports.cgi index ac77c89fd..f557ac7f6 100755 --- a/reports.cgi +++ b/reports.cgi @@ -38,7 +38,6 @@ # Myk Melez # Matthew Tuck -use diagnostics; use strict; use lib qw(.); diff --git a/show_activity.cgi b/show_activity.cgi index 59e43b672..f6d9b75c9 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -22,7 +22,6 @@ # Myk Melez # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/show_bug.cgi b/show_bug.cgi index f832a2930..6711ff7e6 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -20,7 +20,6 @@ # # Contributor(s): Terry Weissman -use diagnostics; use strict; use lib qw(.); diff --git a/showattachment.cgi b/showattachment.cgi index 4aaf6f17d..0c93483bd 100755 --- a/showattachment.cgi +++ b/showattachment.cgi @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # Jacob Steenhagen -use diagnostics; use strict; use lib qw(.); diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index 2a5d20f6c..e5885d180 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/showdependencytree.cgi b/showdependencytree.cgi index 9b7060591..7917f00fe 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -23,7 +23,6 @@ # Christian Reis # Myk Melez -use diagnostics; use strict; use lib qw(.); diff --git a/sidebar.cgi b/sidebar.cgi index 2805547cf..4f40d3cc0 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -16,7 +16,6 @@ # Contributor(s): Jacob Steenhagen use strict; -use diagnostics; use lib "."; require "CGI.pl"; diff --git a/syncshadowdb b/syncshadowdb index 18c4528dc..ffe87eea3 100755 --- a/syncshadowdb +++ b/syncshadowdb @@ -21,7 +21,6 @@ # Contributor(s): Terry Weissman # David Gardiner -use diagnostics; use strict; require "globals.pl"; diff --git a/t/Support/Templates.pm b/t/Support/Templates.pm index 07f46f700..c12f6ddcc 100644 --- a/t/Support/Templates.pm +++ b/t/Support/Templates.pm @@ -23,7 +23,6 @@ package Support::Templates; -use diagnostics; use strict; use lib 't'; diff --git a/token.cgi b/token.cgi index 2225e4f8b..43ee1b04e 100755 --- a/token.cgi +++ b/token.cgi @@ -25,7 +25,6 @@ ############################################################################ # Make it harder for us to do dangerous things in Perl. -use diagnostics; use strict; use lib qw(.); diff --git a/userprefs.cgi b/userprefs.cgi index cf58cf265..33b377574 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -20,7 +20,6 @@ # Christopher Aillon # Gervase Markham -use diagnostics; use strict; use lib qw(.); diff --git a/votes.cgi b/votes.cgi index 513add4c3..88e303971 100755 --- a/votes.cgi +++ b/votes.cgi @@ -23,7 +23,6 @@ # Christopher Aillon # Gervase Markham -use diagnostics; use strict; use lib "."; diff --git a/whineatnews.pl b/whineatnews.pl index b364836a5..2a3bdf1b9 100755 --- a/whineatnews.pl +++ b/whineatnews.pl @@ -26,7 +26,6 @@ # assigned to them that has status NEW that has not been touched for # more than 7 days. -use diagnostics; use strict; require "globals.pl"; diff --git a/xml.cgi b/xml.cgi index 8a0bbf1e4..219a39c74 100755 --- a/xml.cgi +++ b/xml.cgi @@ -22,7 +22,6 @@ # Terry Weissman # Gervase Markham -use diagnostics; use strict; use lib qw(.); -- cgit v1.2.3-65-gdbad From 618851e70c753221a79942976523493b6c52fc71 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Fri, 13 Sep 2002 05:51:42 +0000 Subject: Bug 167978 - Fix Throw*Error l10n regressions and add a test to catch more. Patch by gerv; r=bbaetz. --- attachment.cgi | 2 +- describecomponents.cgi | 5 ++++- duplicates.cgi | 4 ++-- post_bug.cgi | 4 ++-- process_bug.cgi | 3 +-- quips.cgi | 2 +- t/002goodperl.t | 28 ++++++++++++++++++++++++- template/en/default/global/user-error.html.tmpl | 21 +++++++++++++++++++ userprefs.cgi | 3 +-- 9 files changed, 60 insertions(+), 12 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 43b2b4940..45538ae23 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -142,7 +142,7 @@ sub validateID my ($bugid, $isprivate) = FetchSQLData(); ValidateBugID($bugid); if (($isprivate > 0 ) && Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { - ThrowUserError("You are not permitted access to this attachment."); + ThrowUserError("attachment_access_denied"); } } diff --git a/describecomponents.cgi b/describecomponents.cgi index 9edb5dfa0..be3be4b9f 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -87,7 +87,10 @@ my $product = $::FORM{'product'}; # cannot get any other information about that product. my $product_id = get_product_id($product); -ThrowUserError("The product name is invalid.") unless $product_id; +if (!$product_id) { + $::vars->{'product'} = $product; + ThrowUserError("invalid_product_name"); +} # Make sure the user is authorized to access this product. if (Param("usebuggroups") && GroupExists($product)) { diff --git a/duplicates.cgi b/duplicates.cgi index 285c65717..5196027ac 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -65,8 +65,8 @@ my $product_id; if ($product) { $product_id = get_product_id($product); if (!$product_id) { - ThrowUserError("The product " . html_quote($product) . - " does not exist"); + $vars->{'product'} = $product; + ThrowUserError("invalid_product_name"); } } diff --git a/post_bug.cgi b/post_bug.cgi index bab55a9ac..b7fdf66d5 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -71,8 +71,8 @@ ValidateComment($comment); my $product = $::FORM{'product'}; my $product_id = get_product_id($product); if (!$product_id) { - ThrowUserError("Sorry, the product " . html_quote($product) . - " does not exist"); + $vars->{'product'} = $product; + ThrowUserError("invalid_product_name"); } # Set cookies diff --git a/process_bug.cgi b/process_bug.cgi index 59bb83e0e..ab65c0da5 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -558,8 +558,7 @@ if ($::FORM{'product'} ne $::dontchange) { my $comp_id; # Remember, can't use this for mass changes if ($::FORM{'component'} ne $::dontchange) { if (!defined $prod_id) { - ThrowUserError("You cannot change the component from a list of bugs " . - "covering more than one product"); + ThrowUserError("no_component_change_for_multiple_products"); } $comp_id = get_component_id($prod_id, $::FORM{'component'}); diff --git a/quips.cgi b/quips.cgi index 7db312c61..f120e5a24 100755 --- a/quips.cgi +++ b/quips.cgi @@ -63,7 +63,7 @@ if ($action eq "add") { } if (Param('enablequips') ne "on") { - ThrowUserError("This site does not permit the addition of new quips"); + ThrowUserError("no_new_quips"); exit(); } diff --git a/t/002goodperl.t b/t/002goodperl.t index 1431e7fe8..973a5fb88 100644 --- a/t/002goodperl.t +++ b/t/002goodperl.t @@ -44,7 +44,7 @@ use lib 't'; use Support::Files; -use Test::More tests => (scalar(@Support::Files::testitems) * 2); +use Test::More tests => (scalar(@Support::Files::testitems) * 3); my @testitems = @Support::Files::testitems; # get the files to test. @@ -116,4 +116,30 @@ foreach my $file (@testitems) { } } +# Check to see that all error messages use tags (for l10n reasons.) +foreach my $file (@testitems) { + $file =~ s/\s.*$//; # nuke everything after the first space (#comment) + next if (!$file); # skip null entries + if (! open (FILE, $file)) { + ok(0,"could not open $file --WARNING"); + next; + } + my $lineno = 0; + my $error = 0; + + while (my $file_line = ) { + $lineno++; + if ($file_line =~ /Throw.*Error\("(.*?)"/) { + if ($1 =~ /\s/) { + ok(0,"$file has a Throw*Error call on line $lineno + which doesn't use a tag --ERROR"); + $error = 1; + } + } + } + + ok(1,"$file uses Throw*Error calls correctly") if !$error; + + close(FILE); +} exit 0; diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 64b354219..4edacf2c8 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -75,6 +75,10 @@ Bug aliases cannot be longer than 20 characters. Please choose a shorter alias. + [% ELSIF error == "attachment_access_denied" %] + [% title = "Access Denied" %] + You are not permitted access to this attachment. + [% ELSIF error == "buglist_parameters_required" %] [% title = "Parameters Required" %] [% url = "query.cgi" %] @@ -155,6 +159,10 @@ [% title = "Invalid Bug ID" %] The bug id [% bug_id FILTER html %] is invalid. + [% ELSIF error == "invalid_product_name" %] + [% title = "Invalid Product Name" %] + The product name '[% product FILTER html %]' is invalid or does not exist. + [% ELSIF error == "invalid_username" %] [% title = "Invalid Username" %] The name [% name FILTER html %] is not a valid username. @@ -182,6 +190,11 @@ [% title = "No Bugs Chosen" %] You apparently didn't choose any bugs to modify. + [% ELSIF error == "no_component_change_for_multiple_products" %] + [% title = "Action Not Permitted" %] + You cannot change the component for a list of bugs covering more than + one product. + [% ELSIF error == "no_dupe_stats" %] [% title = "Cannot Find Duplicate Statistics" %] There are no duplicate statistics for today ([% today %]) or yesterday. @@ -200,6 +213,10 @@ There are no duplicate statistics for today ([% today %]), and an error occurred opening yesterday's dupes file: [% error_msg FILTER html %]. + [% ELSIF error == "no_new_quips" %] + [% title = "No New Quips" %] + This site does not permit the addition of new quips. + [% ELSIF error == "page_not_found" %] [% title = "Page not found" %] The page you requested cannot be found. @@ -214,6 +231,10 @@ [% title = "Unknown Keyword" %] [% keyword FILTER html %] is not a known keyword. The legal keyword names are listed here. + + [% ELSIF error == "unknown_tab" %] + [% title = "Unknown Tab" %] + [% current_tab_name FILTER html %] is not a legal tab name. [% ELSE %] [%# Cope with legacy calling convention, where "error" was the string diff --git a/userprefs.cgi b/userprefs.cgi index 33b377574..808aebf40 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -395,8 +395,7 @@ SWITCH: for ($current_tab_name) { DoPermissions(); last SWITCH; }; - ThrowUserError("Unknown tab " . html_quote($current_tab_name) - . ""); + ThrowUserError("current_tab_name"); } # Generate and return the UI (HTML page) from the appropriate template. -- cgit v1.2.3-65-gdbad From a29e0e0640faad00f8c1bf792b93260047f16956 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Sat, 21 Sep 2002 05:26:11 +0000 Subject: Bug 163114 - Templatise all calls to DisplayError. First patch - attachment.cgi. Patch by gerv; r=burnus. --- attachment.cgi | 105 +++++++++--------------- template/en/default/global/code-error.html.tmpl | 21 +++++ template/en/default/global/user-error.html.tmpl | 58 +++++++++++++ 3 files changed, 118 insertions(+), 66 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 45538ae23..fcbe86a78 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -114,7 +114,7 @@ elsif ($action eq "update") } else { - DisplayError("I could not figure out what you wanted to do.") + ThrowCodeError("unknown_action"); } exit; @@ -128,15 +128,15 @@ sub validateID # Validate the value of the "id" form field, which must contain an # integer that is the ID of an existing attachment. - detaint_natural($::FORM{'id'}) - || DisplayError("You did not enter a valid attachment number.") - && exit; + $vars->{'attach_id'} = $::FORM{'id'}; + + detaint_natural($::FORM{'id'}) + || ThrowUserError("invalid_attach_id"); # Make sure the attachment exists in the database. SendSQL("SELECT bug_id, isprivate FROM attachments WHERE attach_id = $::FORM{'id'}"); MoreSQLData() - || DisplayError("Attachment #$::FORM{'id'} does not exist.") - && exit; + || ThrowUserError("invalid_attach_id"); # Make sure the user is authorized to access this attachment's bug. my ($bugid, $isprivate) = FetchSQLData(); @@ -164,15 +164,13 @@ sub validateCanEdit "attach_id = $attach_id AND submitter_id = $::userid"); FetchSQLData() - || DisplayError("You are not authorised to edit attachment #$attach_id") - && exit; + || ThrowUserError("illegal_attachment_edit"); } sub validateDescription { $::FORM{'description'} - || DisplayError("You must enter a description for the attachment.") - && exit; + || ThrowUserError("missing_attachment_description"); } sub validateIsPatch @@ -190,10 +188,7 @@ sub validateContentType { if (!$::FORM{'contenttypemethod'}) { - DisplayError("You must choose a method for determining the content type, - either auto-detect, select from list, or enter - manually."); - exit; + ThrowUserError("missing_content_type_method"); } elsif ($::FORM{'contenttypemethod'} eq 'autodetect') { @@ -201,10 +196,7 @@ sub validateContentType # specified in the HTTP request headers. if ( !$::FILE{'data'}->{'contenttype'} ) { - DisplayError("You asked Bugzilla to auto-detect the content type, but - your browser did not specify a content type when uploading the file, - so you must enter a content type manually."); - exit; + ThrowUserError("missing_content_type"); } $::FORM{'contenttype'} = $::FILE{'data'}->{'contenttype'}; } @@ -220,22 +212,14 @@ sub validateContentType } else { - my $htmlcontenttypemethod = html_quote($::FORM{'contenttypemethod'}); - DisplayError("Your form submission got corrupted somehow. The content - method field, which specifies how the content type gets determined, - should have been either autodetect, list, - or manual, but was instead $htmlcontenttypemethod."); - exit; + $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; + ThrowCodeError("illegal_content_type_method"); } if ( $::FORM{'contenttype'} !~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ ) { - my $htmlcontenttype = html_quote($::FORM{'contenttype'}); - DisplayError("The content type $htmlcontenttype is invalid. - Valid types must be of the form foo/bar where foo - is either application, audio, image, message, model, multipart, - text, or video."); - exit; + $vars->{'contenttype'} = $::FORM{'contenttype'}; + ThrowUserError("invalid_content_type"); } } @@ -271,9 +255,8 @@ sub validateStatuses foreach my $status (@{$::MFORM{'status'}}) { grep($_ == $status, @statusdefs) - || DisplayError("One of the statuses you entered is not a valid status - for this attachment.") - && exit; + || ThrowUserError("invalid_attach_status"); + # We have tested that the status is valid, so it can be detainted detaint_natural($status); } @@ -282,8 +265,7 @@ sub validateStatuses sub validateData { $::FORM{'data'} - || DisplayError("The file you are trying to attach is empty!") - && exit; + || ThrowUserError("zero_length_file"); my $len = length($::FORM{'data'}); @@ -294,27 +276,18 @@ sub validateData # the "maxattachmentsize" parameter. if ( $::FORM{'ispatch'} && $maxpatchsize && $len > $maxpatchsize*1024 ) { - my $lenkb = sprintf("%.0f", $len/1024); - DisplayError("The file you are trying to attach is ${lenkb} kilobytes (KB) in size. - Patches cannot be more than ${maxpatchsize}KB in size. - Try breaking your patch into several pieces."); - exit; + $vars->{'filesize'} = sprintf("%.0f", $len/1024); + ThrowUserError("patch_too_large"); } elsif ( !$::FORM{'ispatch'} && $maxattachmentsize && $len > $maxattachmentsize*1024 ) { - my $lenkb = sprintf("%.0f", $len/1024); - DisplayError("The file you are trying to attach is ${lenkb} kilobytes (KB) in size. - Non-patch attachments cannot be more than ${maxattachmentsize}KB. - If your attachment is an image, try converting it to a compressable - format like JPG or PNG, or put it elsewhere on the web and - link to it from the bug's URL field or in a comment on the bug."); - exit; + $vars->{'filesize'} = sprintf("%.0f", $len/1024); + ThrowUserError("file_too_large"); } } sub validateFilename { defined $::FILE{'data'} - || DisplayError("You did not specify a file to attach.") - && exit; + || ThrowUserError("file_not_specified"); } sub validateObsolete @@ -322,35 +295,32 @@ sub validateObsolete # Make sure the attachment id is valid and the user has permissions to view # the bug to which it is attached. foreach my $attachid (@{$::MFORM{'obsolete'}}) { + $vars->{'attach_id'} = $attachid; + detaint_natural($attachid) - || DisplayError("The attachment number of one of the attachments - you wanted to obsolete is invalid.") - && exit; + || ThrowCodeError("invalid_attach_id_to_obsolete"); SendSQL("SELECT bug_id, isobsolete, description FROM attachments WHERE attach_id = $attachid"); # Make sure the attachment exists in the database. MoreSQLData() - || DisplayError("Attachment #$attachid does not exist.") - && exit; + || ThrowUserError("invalid_attach_id"); my ($bugid, $isobsolete, $description) = FetchSQLData(); + $vars->{'description'} = $description; + if ($bugid != $::FORM{'bugid'}) { - $description = html_quote($description); - DisplayError("Attachment #$attachid ($description) is attached - to bug #$bugid, but you tried to flag it as obsolete while - creating a new attachment to bug #$::FORM{'bugid'}."); - exit; + $vars->{'my_bug_id'} = $::FORM{'bugid'}; + $vars->{'attach_bug_id'} = $bugid; + ThrowCodeError("mismatched_bug_ids_on_obsolete"); } if ( $isobsolete ) { - $description = html_quote($description); - DisplayError("Attachment #$attachid ($description) is already obsolete."); - exit; + ThrowCodeError("attachment_already_obsolete"); } # Check that the user can modify this attachment @@ -632,10 +602,13 @@ sub update # Get the bug ID for the bug to which this attachment is attached. SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); - my $bugid = FetchSQLData() - || DisplayError("Cannot figure out bug number.") - && exit; - + my $bugid = FetchSQLData(); + unless ($bugid) + { + $vars->{'bug_id'} = $bugid; + ThrowUserError("invalid_bug_id"); + } + # Lock database tables in preparation for updating the attachment. SendSQL("LOCK TABLES attachments WRITE , attachstatuses WRITE , attachstatusdefs READ , fielddefs READ , bugs_activity WRITE"); diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index a74d014cd..55fac2aca 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -40,6 +40,10 @@ to any [% parameters %] which you may have set before calling ThrowCodeError. + [% ELSIF error == "attachment_already_obsolete" %] + Attachment #[% attachid FILTER html %] ([% description FILTER html %]) + is already obsolete. + [% ELSIF error == "field_type_mismatch" %] Cannot seem to handle [% field %] and [% type %] together. @@ -47,6 +51,13 @@ [% ELSIF error == "group_bit_invalid" %] One of the group bits submitted was invalid. + [% ELSIF error == "illegal_content_type_method" %] + Your form submission got corrupted somehow. The content + method field, which specifies how the content type gets determined, + should have been either autodetect, list, + or manual, but was instead + [% contenttypemethod FILTER html %]. + [% ELSIF error == "illegal_field" %] A legal [% field FILTER html %] was not set. @@ -54,6 +65,16 @@ Attempted to add bug to an inactive group, identified by the bit '[% bit FILTER html %]'. + [% ELSIF error == "invalid_attach_id_to_obsolete" %] + The attachment number of one of the attachments you wanted to obsolete, + [% attach_id FILTER html %], is invalid. + + [% ELSIF error == "mismatched_bug_ids_on_obsolete" %] + Attachment [% attach_id FILTER html %] ([% description FILTER html %]) + is attached to bug [% attach_bug_id FILTER html %], but you tried to + flag it as obsolete while creating a new attachment to bug + [% my_bug_id FILTER html %]. + [% ELSIF error == "no_bug_data" %] No data when fetching bug [% bug_id %]. diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 6c0b37021..c6f970df3 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -116,11 +116,28 @@ really make sense to mark a bug as a duplicate of itself, does it? + [% ELSIF error == "file_not_specified" %] + [% title = "No File Specified" %] + You did not specify a file to attach. + + [% ELSIF error == "file_too_large" %] + [% title = "File Too Large" %] + The file you are trying to attach is [% filesize %] kilobytes (KB) in size. + Non-patch attachments cannot be more than [% Param('maxattachmentsize') %] + KB. + If your attachment is an image, try converting it to a compressable + format like JPG or PNG, or put it elsewhere on the web and + link to it from the bug's URL field or in a comment on the bug. + [% ELSIF error == "illegal_at_least_x_votes" %] [% title = "Your Query Makes No Sense" %] The At least ___ votes field must be a simple number. You entered [% value FILTER html %], which isn't. + [% ELSIF error == "illegal_attachment_edit" %] + [% title = "Unauthorised Action" %] + You are not authorised to edit attachment [% attach_id %]. + [% ELSIF error == "illegal_attachment_is_patch" %] [% title = "Your Query Makes No Sense" %] The only legal values for the Attachment is patch field are @@ -155,6 +172,21 @@ The only legal values for the Attachment is obsolete field are 0 and 1. + [% ELSIF error == "invalid_attach_id" %] + [% title = "Invalid Attachment ID" %] + The attachment id [% attach_id FILTER html %] is invalid. + + [% ELSIF error == "invalid_attach_status" %] + [% title = "Invalid Attachment Status" %] + One of the statuses you entered is not a valid status for this attachment. + + [% ELSIF error == "invalid_content_type" %] + [% title = "Invalid Content-Type" %] + The content type [% contenttype FILTER html %] is invalid. + Valid types must be of the form foo/bar where foo + is either application, audio, image, message, model, multipart, + text, or video. + [% ELSIF error == "invalid_bug_id" %] [% title = "Invalid Bug ID" %] The bug id [% bug_id FILTER html %] is invalid. @@ -175,6 +207,22 @@ if you are going to accept it. Part of accepting a bug is giving an estimate of when it will be fixed. + [% ELSIF error == "missing_attachment_description" %] + [% title = "Missing Attachment Description" %] + You must enter a description for the attachment. + + [% ELSIF error == "missing_content_type" %] + [% title = "Missing Content-Type" %] + You asked Bugzilla to auto-detect the content type, but + your browser did not specify a content type when uploading the file, + so you must enter a content type manually. + + [% ELSIF error == "missing_content_type_method" %] + [% title = "Missing Content-Type Determination Method" %] + You must choose a method for determining the content type, + either auto-detect, select from list, or enter + manually. + [% ELSIF error == "missing_email_type" %] [% title = "Your Query Makes No Sense" %] You must specify one or more fields in which to search for @@ -221,6 +269,12 @@ [% title = "No Page Specified" %] You did not specify the id of a page to display. + [% ELSIF error == "patch_too_large" %] + [% title = "File Too Large" %] + The file you are trying to attach is [% filesize %] kilobytes (KB) in size. + Patches cannot be more than [% Param('maxpatchsize') %] KB in size. + Try breaking your patch into several pieces. + [% ELSIF error == "reassign_to_empty" %] [% title = "Illegal Reassignment" %] You cannot reassign to a bug to nobody. Unless you @@ -236,6 +290,10 @@ [% title = "Unknown Tab" %] [% current_tab_name FILTER html %] is not a legal tab name. + [% ELSIF error == "zero_length_file" %] + [% title = "File Is Empty" %] + The file you are trying to attach is empty! + [% ELSE %] [%# Cope with legacy calling convention, where "error" was the string # to print. -- cgit v1.2.3-65-gdbad From cf9b4ba2e757925eeb18bb63411ae30c8600c643 Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Sat, 21 Sep 2002 23:57:07 +0000 Subject: Fix for bug 63601: Recommend filename when downloading attachments (except in IE4, which chokes on the Content-Disposition header) Patch by Daniel Raichle and Dave Miller r= bbaetz, gerv --- attachment.cgi | 39 ++++++++++++++++++++------- checksetup.pl | 1 + template/en/default/attachment/edit.html.tmpl | 3 +++ 3 files changed, 34 insertions(+), 9 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index fcbe86a78..3b29a321e 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -20,6 +20,8 @@ # # Contributor(s): Terry Weissman # Myk Melez +# Daniel Raichle +# Dave Miller ################################################################################ # Script Initialization @@ -338,13 +340,23 @@ sub view # Display an attachment. # Retrieve the attachment content and its content type from the database. - SendSQL("SELECT mimetype, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($contenttype, $thedata) = FetchSQLData(); + SendSQL("SELECT mimetype, filename, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); + my ($contenttype, $filename, $thedata) = FetchSQLData(); + + # Determine if the browser supports the Content-Disposition header or not + my $usedisposition = 1; # assume yes, unless we discover otherwise + if ($::ENV{HTTP_USER_AGENT} =~ /^Mozilla.*MSIE (\d+)/) { + if ($1 < 5) { $usedisposition = 0; } # MSIE < 5.0 chokes on it + } # Return the appropriate HTTP response headers. - print "Content-Type: $contenttype\n\n"; + $filename =~ s/^.*[\/\\]//; + my $filesize = length($thedata); + print qq{Content-Type: $contenttype; name="$filename"\n}; + print qq{Content-Disposition: attachment; filename=$filename\n} if $usedisposition; + print qq{Content-Length: $filesize\n}; + print qq{\n$thedata}; - print $thedata; } @@ -529,9 +541,9 @@ sub edit # Users cannot edit the content of the attachment itself. # Retrieve the attachment from the database. - SendSQL("SELECT description, mimetype, bug_id, ispatch, isobsolete, isprivate + SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isobsolete, isprivate FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($description, $contenttype, $bugid, $ispatch, $isobsolete, $isprivate) = FetchSQLData(); + my ($description, $contenttype, $filename, $bugid, $ispatch, $isobsolete, $isprivate) = FetchSQLData(); # Flag attachment as to whether or not it can be viewed (as opposed to # being downloaded). Currently I decide it is viewable if its content @@ -577,6 +589,7 @@ sub edit $vars->{'attachid'} = $::FORM{'id'}; $vars->{'description'} = $description; $vars->{'contenttype'} = $contenttype; + $vars->{'filename'} = $filename; $vars->{'bugid'} = $bugid; $vars->{'bugsummary'} = $bugsummary; $vars->{'ispatch'} = $ispatch; @@ -614,10 +627,10 @@ sub update attachstatusdefs READ , fielddefs READ , bugs_activity WRITE"); # Get a copy of the attachment record before we make changes # so we can record those changes in the activity table. - SendSQL("SELECT description, mimetype, ispatch, isobsolete, isprivate + SendSQL("SELECT description, mimetype, filename, ispatch, isobsolete, isprivate FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($olddescription, $oldcontenttype, $oldispatch, $oldisobsolete, - $oldisprivate ) = FetchSQLData(); + my ($olddescription, $oldcontenttype, $oldfilename, $oldispatch, + $oldisobsolete, $oldisprivate) = FetchSQLData(); # Get the list of old status flags. SendSQL("SELECT attachstatusdefs.name @@ -657,12 +670,14 @@ sub update # Quote the description and content type for use in the SQL UPDATE statement. my $quoteddescription = SqlQuote($::FORM{'description'}); my $quotedcontenttype = SqlQuote($::FORM{'contenttype'}); + my $quotedfilename = SqlQuote($::FORM{'filename'}); # Update the attachment record in the database. # Sets the creation timestamp to itself to avoid it being updated automatically. SendSQL("UPDATE attachments SET description = $quoteddescription , mimetype = $quotedcontenttype , + filename = $quotedfilename , ispatch = $::FORM{'ispatch'} , isobsolete = $::FORM{'isobsolete'} , isprivate = $::FORM{'isprivate'} @@ -682,6 +697,12 @@ sub update SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldcontenttype, $quotedcontenttype)"); } + if ($oldfilename ne $::FORM{'filename'}) { + my $quotedoldfilename = SqlQuote($oldfilename); + my $fieldid = GetFieldID('attachments.filename'); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldfilename, $quotedfilename)"); + } if ($oldispatch ne $::FORM{'ispatch'}) { my $fieldid = GetFieldID('attachments.ispatch'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) diff --git a/checksetup.pl b/checksetup.pl index 803d64d14..c00301742 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1805,6 +1805,7 @@ AddFDef("dependson", "BugsThisDependsOn", 0); AddFDef("blocked", "OtherBugsDependingOnThis", 0); AddFDef("attachments.description", "Attachment description", 0); AddFDef("attachments.thedata", "Attachment data", 0); +AddFDef("attachments.filename", "Attachment filename", 0); AddFDef("attachments.mimetype", "Attachment mime type", 0); AddFDef("attachments.ispatch", "Attachment is patch", 0); AddFDef("attachments.isobsolete", "Attachment is obsolete", 0); diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index c9bc7c1cd..ec2616bf9 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -153,6 +153,9 @@ Description:

+ Filename:
+
+ MIME Type:

-- cgit v1.2.3-65-gdbad From 65d3dc0ec33fd76229dc02536a74ccac5408876b Mon Sep 17 00:00:00 2001 From: "bugreport%peshkin.net" <> Date: Mon, 23 Sep 2002 00:14:48 +0000 Subject: bug 157756 - Groups_20020716_Branch Tracking : > 55 groups now supported r=bbaetz, gerv --- Bug.pm | 41 +- Bugzilla/Bug.pm | 41 +- Bugzilla/Search.pm | 36 +- CGI.pl | 69 +- attachment.cgi | 1 - bug_form.pl | 108 +-- buglist.cgi | 29 +- checksetup.pl | 794 ++++++++++++++------- contrib/bug_email.pl | 2 +- docs/sgml/administration.sgml | 64 +- docs/xml/administration.xml | 64 +- duplicates.cgi | 7 +- editgroups.cgi | 506 ++++++------- editproducts.cgi | 185 +---- editusers.cgi | 286 ++++---- enter_bug.cgi | 92 +-- globals.pl | 369 ++++++---- index.cgi | 1 - long_list.cgi | 6 +- post_bug.cgi | 28 +- process_bug.cgi | 103 +-- processmail | 35 +- query.cgi | 26 +- sanitycheck.cgi | 90 ++- show_activity.cgi | 5 +- showdependencygraph.cgi | 16 +- showdependencytree.cgi | 23 +- sidebar.cgi | 6 +- .../en/default/account/prefs/permissions.html.tmpl | 34 +- template/en/default/global/useful-links.html.tmpl | 2 +- template/en/default/sidebar.xul.tmpl | 2 +- token.cgi | 2 + userprefs.cgi | 28 +- votes.cgi | 3 +- 34 files changed, 1695 insertions(+), 1409 deletions(-) (limited to 'attachment.cgi') diff --git a/Bug.pm b/Bug.pm index 7e703d14f..3dadd3cd5 100755 --- a/Bug.pm +++ b/Bug.pm @@ -37,8 +37,8 @@ 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 groupset - delta_ts votes whoid usergroupset comment query error) ){ + qa_contact status_whiteboard creation_ts + delta_ts votes whoid comment query error) ){ $ok_field{$key}++; } @@ -105,10 +105,6 @@ sub initBug { $self->{'whoid'} = $user_id; - &::SendSQL("SELECT groupset FROM profiles WHERE userid=$self->{'whoid'}"); - my $usergroupset = &::FetchOneColumn(); - if (!$usergroupset) { $usergroupset = '0' } - $self->{'usergroupset'} = $usergroupset; my $query = " select @@ -116,7 +112,7 @@ sub initBug { 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'), - groupset, delta_ts, sum(votes.count) + delta_ts, sum(votes.count) from bugs left join votes using(bug_id), products, components where bugs.bug_id = $bug_id @@ -124,10 +120,10 @@ sub initBug { AND components.id = bugs.component_id group by bugs.bug_id"; - &::SendSQL(&::SelectVisible($query, $user_id, $usergroupset)); - my @row; + &::SendSQL($query); + my @row = (); - if (@row = &::FetchSQLData()) { + if ((@row = &::FetchSQLData()) && &::CanSeeBug($bug_id, $self->{'whoid'})) { my $count = 0; my %fields; foreach my $field ("bug_id", "alias", "product", "version", "rep_platform", @@ -135,24 +131,21 @@ sub initBug { "bug_severity", "component", "assigned_to", "reporter", "bug_file_loc", "short_desc", "target_milestone", "qa_contact", "status_whiteboard", "creation_ts", - "groupset", "delta_ts", "votes") { + "delta_ts", "votes") { $fields{$field} = shift @row; if ($fields{$field}) { $self->{$field} = $fields{$field}; } $count++; } - } else { - &::SendSQL("select groupset from bugs where bug_id = $bug_id"); - if (@row = &::FetchSQLData()) { + } elsif (@row) { $self->{'bug_id'} = $bug_id; $self->{'error'} = "NotPermitted"; return $self; - } else { + } else { $self->{'bug_id'} = $bug_id; $self->{'error'} = "NotFound"; return $self; - } } $self->{'assigned_to'} = &::DBID_to_name($self->{'assigned_to'}); @@ -356,22 +349,6 @@ sub XML_Footer { return ("\n"); } -sub UserInGroup { - my $self = shift(); - my ($groupname) = (@_); - if ($self->{'usergroupset'} eq "0") { - return 0; - } - &::ConnectToDatabase(); - &::SendSQL("select (bit & $self->{'usergroupset'}) != 0 from groups where name = " - . &::SqlQuote($groupname)); - my $bit = &::FetchOneColumn(); - if ($bit) { - return 1; - } - return 0; -} - sub CanChangeField { my $self = shift(); my ($f, $oldvalue, $newvalue) = (@_); diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 7e703d14f..3dadd3cd5 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -37,8 +37,8 @@ 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 groupset - delta_ts votes whoid usergroupset comment query error) ){ + qa_contact status_whiteboard creation_ts + delta_ts votes whoid comment query error) ){ $ok_field{$key}++; } @@ -105,10 +105,6 @@ sub initBug { $self->{'whoid'} = $user_id; - &::SendSQL("SELECT groupset FROM profiles WHERE userid=$self->{'whoid'}"); - my $usergroupset = &::FetchOneColumn(); - if (!$usergroupset) { $usergroupset = '0' } - $self->{'usergroupset'} = $usergroupset; my $query = " select @@ -116,7 +112,7 @@ sub initBug { 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'), - groupset, delta_ts, sum(votes.count) + delta_ts, sum(votes.count) from bugs left join votes using(bug_id), products, components where bugs.bug_id = $bug_id @@ -124,10 +120,10 @@ sub initBug { AND components.id = bugs.component_id group by bugs.bug_id"; - &::SendSQL(&::SelectVisible($query, $user_id, $usergroupset)); - my @row; + &::SendSQL($query); + my @row = (); - if (@row = &::FetchSQLData()) { + if ((@row = &::FetchSQLData()) && &::CanSeeBug($bug_id, $self->{'whoid'})) { my $count = 0; my %fields; foreach my $field ("bug_id", "alias", "product", "version", "rep_platform", @@ -135,24 +131,21 @@ sub initBug { "bug_severity", "component", "assigned_to", "reporter", "bug_file_loc", "short_desc", "target_milestone", "qa_contact", "status_whiteboard", "creation_ts", - "groupset", "delta_ts", "votes") { + "delta_ts", "votes") { $fields{$field} = shift @row; if ($fields{$field}) { $self->{$field} = $fields{$field}; } $count++; } - } else { - &::SendSQL("select groupset from bugs where bug_id = $bug_id"); - if (@row = &::FetchSQLData()) { + } elsif (@row) { $self->{'bug_id'} = $bug_id; $self->{'error'} = "NotPermitted"; return $self; - } else { + } else { $self->{'bug_id'} = $bug_id; $self->{'error'} = "NotFound"; return $self; - } } $self->{'assigned_to'} = &::DBID_to_name($self->{'assigned_to'}); @@ -356,22 +349,6 @@ sub XML_Footer { return ("\n"); } -sub UserInGroup { - my $self = shift(); - my ($groupname) = (@_); - if ($self->{'usergroupset'} eq "0") { - return 0; - } - &::ConnectToDatabase(); - &::SendSQL("select (bit & $self->{'usergroupset'}) != 0 from groups where name = " - . &::SqlQuote($groupname)); - my $bit = &::FetchOneColumn(); - if ($bit) { - return 1; - } - return 0; -} - sub CanChangeField { my $self = shift(); my ($f, $oldvalue, $newvalue) = (@_); diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 482daca5c..d6e7a9b7f 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -29,7 +29,7 @@ use strict; # The caller MUST require CGI.pl and globals.pl before using this -use vars qw($userid $usergroupset); +use vars qw($userid); package Bugzilla::Search; @@ -117,7 +117,7 @@ sub init { my @legal_fields = ("product", "version", "rep_platform", "op_sys", "bug_status", "resolution", "priority", "bug_severity", "assigned_to", "reporter", "component", - "target_milestone", "groupset"); + "target_milestone", "bug_group"); foreach my $field (keys %F) { if (lsearch(\@legal_fields, $field) != -1) { @@ -322,6 +322,12 @@ sub init { push(@wherepart, "$table.bug_id = bugs.bug_id"); $f = "$table.thetext"; }, + "^bug_group,(?!changed)" => sub { + push(@supptables, "LEFT JOIN bug_group_map bug_group_map_$chartid ON bugs.bug_id = bug_group_map_$chartid.bug_id"); + + push(@supptables, "LEFT JOIN groups groups_$chartid ON groups_$chartid.id = bug_group_map_$chartid.group_id"); + $f = "groups_$chartid.name"; + }, "^attachments\..*," => sub { my $table = "attachments_$chartid"; push(@supptables, "attachments $table"); @@ -747,7 +753,7 @@ sub init { # chart -1 is generated by other code above, not from the user- # submitted form, so we'll blindly accept any values in chart -1 if ((!$chartfields{$f}) && ($chart != -1)) { - my $errstr = "Can't use " . html_quote($f) . " as a field name. " . + my $errstr = "Can't use $f as a field name. " . "If you think you're getting this in error, please copy the " . "entire URL out of the address bar at the top of your browser " . "window and email it to <109679\@bugzilla.org>"; @@ -807,11 +813,27 @@ sub init { $suppseen{$str} = 1; } } - my $query = ("SELECT DISTINCT " . join(', ', @fields) . + my $query = ("SELECT DISTINCT " . + join(', ', @fields) . + ", COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, " . + " COUNT(DISTINCT bgmap.group_id) AS cntbugingroups, " . + " ((COUNT(DISTINCT ccmap.who) AND cclist_accessible) " . + " OR ((bugs.reporter = $::userid) AND bugs.reporter_accessible) " . + " OR bugs.assigned_to = $::userid ) AS canseeanyway " . " FROM $suppstring" . - " WHERE " . join(' AND ', (@wherepart, @andlist))); - - $query = &::SelectVisible($query, $::userid, $::usergroupset); + " LEFT JOIN bug_group_map AS bgmap " . + " ON bgmap.bug_id = bugs.bug_id " . + " LEFT JOIN user_group_map AS ugmap " . + " ON bgmap.group_id = ugmap.group_id " . + " AND ugmap.user_id = $::userid " . + " AND ugmap.isbless = 0" . + " LEFT JOIN cc AS ccmap " . + " ON ccmap.who = $::userid AND ccmap.bug_id = bugs.bug_id " . + " WHERE " . join(' AND ', (@wherepart, @andlist)) . + " GROUP BY bugs.bug_id " . + " HAVING cntuseringroups = cntbugingroups" . + " OR canseeanyway" + ); if ($debug) { print "

" . value_quote($query) . "

\n"; diff --git a/CGI.pl b/CGI.pl index 0c85128d1..70c15c932 100644 --- a/CGI.pl +++ b/CGI.pl @@ -289,11 +289,6 @@ sub ValidateBugID { # converted-from-alias ID. $_[0] = $id; - # Get the values of the usergroupset and userid global variables - # and write them to local variables for use within this function, - # setting those local variables to the default value of zero if - # the global variables are undefined. - # First check that the bug exists SendSQL("SELECT bug_id FROM bugs WHERE bug_id = $id"); @@ -303,7 +298,7 @@ sub ValidateBugID { return if $skip_authorization; - return if CanSeeBug($id, $::userid, $::usergroupset); + return if CanSeeBug($id, $::userid); # The user did not pass any of the authorization tests, which means they # are not authorized to see the bug. Display an error and stop execution. @@ -438,30 +433,25 @@ sub PasswordForLogin { } sub quietly_check_login() { - $::usergroupset = '0'; - my $loginok = 0; $::disabledreason = ''; - $::userid = 0; + my $userid = 0; if (defined $::COOKIE{"Bugzilla_login"} && defined $::COOKIE{"Bugzilla_logincookie"}) { - SendSQL("SELECT profiles.userid, profiles.groupset, " . - "profiles.login_name, " . - "profiles.login_name = " . - SqlQuote($::COOKIE{"Bugzilla_login"}) . - " AND logincookies.ipaddr = " . - SqlQuote($ENV{"REMOTE_ADDR"}) . - ", profiles.disabledtext " . + SendSQL("SELECT profiles.userid," . + " profiles.login_name, " . + " profiles.disabledtext " . " FROM profiles, logincookies WHERE logincookies.cookie = " . SqlQuote($::COOKIE{"Bugzilla_logincookie"}) . - " AND profiles.userid = logincookies.userid"); + " AND profiles.userid = logincookies.userid AND" . + " profiles.login_name = " . + SqlQuote($::COOKIE{"Bugzilla_login"}) . + " AND logincookies.ipaddr = " . + SqlQuote($ENV{"REMOTE_ADDR"})); my @row; - if (@row = FetchSQLData()) { - my ($userid, $groupset, $loginname, $ok, $disabledtext) = (@row); - if ($ok) { + if (MoreSQLData()) { + ($userid, my $loginname, my $disabledtext) = FetchSQLData(); + if ($userid > 0) { if ($disabledtext eq '') { - $loginok = 1; - $::userid = $userid; - $::usergroupset = $groupset; $::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case # is in # canonical form. @@ -469,6 +459,7 @@ sub quietly_check_login() { detaint_natural($::COOKIE{"Bugzilla_logincookie"}); } else { $::disabledreason = $disabledtext; + $userid = 0; } } } @@ -478,13 +469,14 @@ sub quietly_check_login() { my $whoid = DBname_to_id($::FORM{'who'}); delete $::FORM{'who'} unless $whoid; } - if (!$loginok) { + if (!$userid) { delete $::COOKIE{"Bugzilla_login"}; } + $::userid = $userid; + ConfirmGroup($userid); $vars->{'user'} = GetUserInfo($::userid); - - return $loginok; + return $userid; } # Populate a hash with information about this user. @@ -500,10 +492,9 @@ sub GetUserInfo { $user{'login'} = $::COOKIE{"Bugzilla_login"}; $user{'userid'} = $userid; - SendSQL("SELECT mybugslink, realname, groupset, blessgroupset " . + SendSQL("SELECT mybugslink, realname " . "FROM profiles WHERE userid = $userid"); - ($user{'showmybugslink'}, $user{'realname'}, $user{'groupset'}, - $user{'blessgroupset'}) = FetchSQLData(); + ($user{'showmybugslink'}, $user{'realname'}) = FetchSQLData(); SendSQL("SELECT name, query, linkinfooter FROM namedqueries " . "WHERE userid = $userid"); @@ -516,10 +507,15 @@ sub GetUserInfo { $user{'queries'} = \@queries; - SendSQL("select name, (bit & $user{'groupset'}) != 0 from groups"); + $user{'canblessany'} = UserCanBlessAnything(); + + SendSQL("SELECT name FROM groups, user_group_map " . + "WHERE groups.id = user_group_map.group_id " . + "AND user_id = $userid " . + "AND NOT isbless"); while (MoreSQLData()) { - my ($name, $bit) = FetchSQLData(); - $groups{$name} = $bit; + my ($name) = FetchSQLData(); + $groups{$name} = 1; } $user{'groups'} = \%groups; @@ -561,6 +557,7 @@ sub confirm_login { # to a later section. -Joe Robins, 8/3/00 my $enteredlogin = ""; my $realcryptpwd = ""; + my $userid; # If the form contains Bugzilla login and password fields, use Bugzilla's # built-in authentication to authenticate the user (otherwise use LDAP below). @@ -570,7 +567,6 @@ sub confirm_login { CheckEmailSyntax($enteredlogin); # Retrieve the user's ID and crypted password from the database. - my $userid; SendSQL("SELECT userid, cryptpassword FROM profiles WHERE login_name = " . SqlQuote($enteredlogin)); ($userid, $realcryptpwd) = FetchSQLData(); @@ -765,9 +761,9 @@ sub confirm_login { print "Set-Cookie: Bugzilla_logincookie=$logincookie ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; } - my $loginok = quietly_check_login(); + $userid = quietly_check_login(); - if ($loginok != 1) { + if (!$userid) { if ($::disabledreason) { my $cookiepath = Param("cookiepath"); print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT @@ -810,7 +806,8 @@ Content-type: text/html SendSQL("UPDATE logincookies SET lastused = null " . "WHERE cookie = $::COOKIE{'Bugzilla_logincookie'}"); } - return $::userid; + ConfirmGroup($userid); + return $userid; } sub PutHeader { diff --git a/attachment.cgi b/attachment.cgi index 3b29a321e..cfdbd4ea6 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -50,7 +50,6 @@ require "CGI.pl"; ConnectToDatabase(); # Check whether or not the user is logged in and, if so, set the $::userid -# and $::usergroupset variables. quietly_check_login(); ################################################################################ diff --git a/bug_form.pl b/bug_form.pl index 8d7a2b02e..dfffca9b8 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -82,7 +82,7 @@ sub show_bug { bug_file_loc, short_desc, target_milestone, qa_contact, status_whiteboard, date_format(creation_ts,'%Y-%m-%d %H:%i'), - groupset, delta_ts, sum(votes.count), delta_ts calc_disp_date + delta_ts, sum(votes.count), delta_ts calc_disp_date FROM bugs LEFT JOIN votes USING(bug_id), products, components WHERE bugs.bug_id = $id AND bugs.product_id = products.id @@ -106,7 +106,7 @@ sub show_bug { "bug_severity", "component", "assigned_to", "reporter", "bug_file_loc", "short_desc", "target_milestone", "qa_contact", "status_whiteboard", "creation_ts", - "groupset", "delta_ts", "votes","calc_disp_date") + "delta_ts", "votes", "calc_disp_date") { $value = shift(@row); if ($field eq "calc_disp_date") { @@ -221,58 +221,68 @@ sub show_bug { # Groups my @groups; - if ($::usergroupset ne '0' || $bug{'groupset'} ne '0') { - my $bug_groupset = $bug{'groupset'}; - SendSQL("SELECT bit, name, description, (bit & $bug_groupset != 0), - (bit & $::usergroupset != 0) FROM groups - WHERE isbuggroup != 0 " . - # Include active groups as well as inactive groups to which - # the bug already belongs. This way the bug can be removed - # from an inactive group but can only be added to active ones. - "AND ((isactive = 1 AND (bit & $::usergroupset != 0)) OR - (bit & $bug_groupset != 0))"); + # 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" . + " 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" . + " WHERE isbuggroup"); + + $user{'inallgroups'} = 1; - $user{'inallgroups'} = 1; + while (MoreSQLData()) { + my ($groupid, $name, $description, $ison, $ingroup, $isactive) + = FetchSQLData(); + + $bug{'inagroup'} = 1 if ($ison); + + # For product groups, we only want to display the checkbox if either + # (1) The bit is already set, or + # (2) The user is in the group, but either: + # (a) The group is a product group for the current product, or + # (b) The group name isn't a product name + # This means that all product groups will be skipped, but + # non-product bug groups will still be displayed. + if($ison || + ($isactive && ($ingroup && (!Param("usebuggroups") || ($name eq $bug{'product'}) || + (!defined $::proddesc{$name}))))) + { + $user{'inallgroups'} &= $ingroup; - while (MoreSQLData()) { - my ($bit, $name, $description, $ison, $ingroup) = FetchSQLData(); - # For product groups, we only want to display the checkbox if either - # (1) The bit is already set, or - # (2) The user is in the group, but either: - # (a) The group is a product group for the current product, or - # (b) The group name isn't a product name - # This means that all product groups will be skipped, but - # non-product bug groups will still be displayed. - if($ison || - ($ingroup && (($name eq $bug{'product'}) || - (!defined $::proddesc{$name})))) - { - $user{'inallgroups'} &= $ingroup; - - push (@groups, { "bit" => $bit, - "ison" => $ison, - "ingroup" => $ingroup, - "description" => $description }); - } + push (@groups, { "bit" => $groupid, + "ison" => $ison, + "ingroup" => $ingroup, + "description" => $description }); } + } - # If the bug is restricted to a group, display checkboxes 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{'groupset'} != 0) { - $bug{'inagroup'} = 1; - - # 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(); - } + # 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; diff --git a/buglist.cgi b/buglist.cgi index 6597fbe3e..728ead4d1 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -207,23 +207,24 @@ sub GetQuip { return $quip; } -sub GetGroupsByGroupSet { - my ($groupset) = @_; +sub GetGroupsByUserId { + my ($userid) = @_; - return if !$groupset; + return if !$userid; SendSQL(" - SELECT bit, name, description, isactive - FROM groups - WHERE (bit & $groupset) != 0 - AND isbuggroup != 0 + SELECT groups.id, name, description, isactive + FROM groups, user_group_map + WHERE user_id = $userid AND NOT isbless + AND user_group_map.group_id = groups.id + AND isbuggroup ORDER BY description "); my @groups; while (MoreSQLData()) { my $group = {}; - ($group->{'bit'}, $group->{'name'}, + ($group->{'id'}, $group->{'name'}, $group->{'description'}, $group->{'isactive'}) = FetchSQLData(); push(@groups, $group); } @@ -379,7 +380,6 @@ sub DefineColumn { # Column: ID Name Title DefineColumn("id" , "bugs.bug_id" , "ID" ); -DefineColumn("groupset" , "bugs.groupset" , "Groupset" ); DefineColumn("opendate" , "bugs.creation_ts" , "Opened" ); DefineColumn("changeddate" , "bugs.delta_ts" , "Changed" ); DefineColumn("severity" , "bugs.bug_severity" , "Severity" ); @@ -437,9 +437,6 @@ else { # and are hard-coded into the display templates. @displaycolumns = grep($_ ne 'id', @displaycolumns); -# IMPORTANT! Never allow the groupset column to be displayed! -@displaycolumns = grep($_ ne 'groupset', @displaycolumns); - # Add the votes column to the list of columns to be displayed # in the bug list if the user is searching for bugs with a certain # number of votes and the votes column is not already on the list. @@ -458,10 +455,8 @@ if (trim($::FORM{'votes'}) && !grep($_ eq 'votes', @displaycolumns)) { # Generate the list of columns that will be selected in the SQL query. -# The bug ID and groupset are always selected because bug IDs are always -# displayed and we need the groupset to determine whether or not the bug -# is visible to the user. -my @selectcolumns = ("id", "groupset"); +# The bug ID is always selected because bug IDs are always displayed +my @selectcolumns = ("id"); # Display columns are selected because otherwise we could not display them. push (@selectcolumns, @displaycolumns); @@ -721,7 +716,7 @@ if ($dotweak) { $vars->{'bugstatuses'} = [ keys %$bugstatuses ]; # The groups to which the user belongs. - $vars->{'groups'} = GetGroupsByGroupSet($::usergroupset) if $::usergroupset ne '0'; + $vars->{'groups'} = GetGroupsByUserId($::userid); # If all bugs being changed are in the same product, the user can change # their version and component, so generate a list of products, a list of diff --git a/checksetup.pl b/checksetup.pl index c00301742..b752f9b65 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1360,7 +1360,6 @@ $table{attachstatusdefs} = # $table{bugs} = 'bug_id mediumint not null auto_increment primary key, - groupset bigint not null, assigned_to mediumint not null, # This is a comment. bug_file_loc text, bug_severity enum($my_severities) not null, @@ -1454,16 +1453,7 @@ $table{dependencies} = index(dependson)'; -# Group bits must be a power of two. Groups are identified by a bit; sets of -# groups are indicated by or-ing these values together. -# -# isbuggroup is nonzero if this is a group that controls access to a set -# of bugs. In otherword, the groupset field in the bugs table should only -# have this group's bit set if isbuggroup is nonzero. -# -# User regexp is which email addresses are initially put into this group. -# This is only used when an email account is created; otherwise, profiles -# may be individually tweaked to add them in and out of groups. +# User regexp is which email addresses are put into this group. # # 2001-04-10 myk@mozilla.org: # isactive determines whether or not a group is active. An inactive group @@ -1473,14 +1463,14 @@ $table{dependencies} = # http://bugzilla.mozilla.org/show_bug.cgi?id=75482 $table{groups} = - 'bit bigint not null, + 'id mediumint not null auto_increment primary key, name varchar(255) not null, description text not null, isbuggroup tinyint not null, + last_changed datetime not null, userregexp tinytext not null, isactive tinyint not null default 1, - unique(bit), unique(name)'; $table{logincookies} = @@ -1511,13 +1501,10 @@ $table{profiles} = login_name varchar(255) not null, cryptpassword varchar(34), realname varchar(255), - groupset bigint not null, disabledtext mediumtext not null, mybugslink tinyint not null default 1, - blessgroupset bigint not null default 0, emailflags mediumtext, - - + refreshed_when datetime not null, unique(login_name)'; @@ -1610,6 +1597,38 @@ $table{tokens} = index(userid)'; +# group membership tables for tracking group and privilege +# +# This table determines the groups that a user belongs to +# directly or due to regexp and which groups can be blessed +# by a user. +# +# isderived: +# if 0 - record was explicitly granted +# if 1 - record was created by evaluating a regexp or group hierarchy +$table{user_group_map} = + 'user_id mediumint not null, + group_id mediumint not null, + isbless tinyint not null default 0, + isderived tinyint not null default 0, + + unique(user_id, group_id, isderived, isbless)'; + +$table{group_group_map} = + 'member_id mediumint not null, + grantor_id mediumint not null, + isbless tinyint not null default 0, + + unique(member_id, grantor_id, isbless)'; + +# This table determines which groups a user must be a member of +# in order to see a bug. +$table{bug_group_map} = + 'bug_id mediumint not null, + group_id mediumint not null, + unique(bug_id, group_id), + index(group_id)'; + # 2002-07-19, davef@tetsubo.com, bug 67950: # Store quips in the db. $table{quips} = @@ -1617,7 +1636,6 @@ $table{quips} = userid mediumint not null default 0, quip text not null'; - ########################################################################### # Create tables ########################################################################### @@ -1692,7 +1710,7 @@ sub GroupDoesExist ($) # # This subroutine checks if a group exist. If not, it will be automatically -# created with the next available bit set +# created with the next available groupid # sub AddGroup { @@ -1701,57 +1719,19 @@ sub AddGroup { return if GroupDoesExist($name); - # get highest bit number - my $sth = $dbh->prepare("SELECT bit FROM groups ORDER BY bit DESC"); - $sth->execute; - my @row = $sth->fetchrow_array; - - # normalize bits - my $bit; - if (defined $row[0]) { - $bit = $row[0] << 1; - } else { - $bit = 1; - } - - print "Adding group $name ...\n"; - $sth = $dbh->prepare('INSERT INTO groups - (bit, name, description, userregexp, isbuggroup) - VALUES (?, ?, ?, ?, ?)'); - $sth->execute($bit, $name, $desc, $userregexp, 0); - return $bit; -} - - -# -# BugZilla uses --GROUPS-- to assign various rights to its users. -# - -AddGroup 'tweakparams', 'Can tweak operating parameters'; -AddGroup 'editusers', 'Can edit or disable users'; -AddGroup 'creategroups', 'Can create and destroy groups.'; -AddGroup 'editcomponents', 'Can create, destroy, and edit components.'; -AddGroup 'editkeywords', 'Can create, destroy, and edit keywords.'; - -# Add the groupset field here because this code is run before the -# code that updates the database structure. -&AddField('profiles', 'groupset', 'bigint not null'); - -if (!GroupDoesExist("editbugs")) { - my $id = AddGroup('editbugs', 'Can edit all aspects of any bug.', ".*"); - $dbh->do("UPDATE profiles SET groupset = groupset | $id"); -} + my $sth = $dbh->prepare('INSERT INTO groups + (name, description, userregexp, isbuggroup) + VALUES (?, ?, ?, ?)'); + $sth->execute($name, $desc, $userregexp, 0); -if (!GroupDoesExist("canconfirm")) { - my $id = AddGroup('canconfirm', 'Can confirm a bug.', ".*"); - $dbh->do("UPDATE profiles SET groupset = groupset | $id"); + $sth = $dbh->prepare("select last_insert_id()"); + $sth->execute(); + my ($last) = $sth->fetchrow_array(); + return $last; } - - - ########################################################################### # Populate the list of fields. ########################################################################### @@ -1818,9 +1798,9 @@ AddFDef("(to_days(now()) - to_days(bugs.delta_ts))", "Days since bug changed", AddFDef("longdesc", "Comment", 0); AddFDef("alias", "Alias", 0); AddFDef("everconfirmed", "Ever Confirmed", 0); -AddFDef("groupset", "Groupset", 0); AddFDef("reporter_accessible", "Reporter Accessible", 0); AddFDef("cclist_accessible", "CC Accessible", 0); +AddFDef("bug_group", "Group", 0); # Oops. Bug 163299 $dbh->do("DELETE FROM fielddefs WHERE name='cc_accessible'"); @@ -1937,181 +1917,9 @@ CheckEnumField('bugs', 'op_sys', @my_opsys); CheckEnumField('bugs', 'rep_platform', @my_platforms); -########################################################################### -# Create Administrator --ADMIN-- -########################################################################### - -# Prompt the user for the email address and name of an administrator. Create -# that login, if it doesn't exist already, and make it a member of all groups. - -sub bailout { # this is just in case we get interrupted while getting passwd - system("stty","echo"); # re-enable input echoing - exit 1; -} - -$sth = $dbh->prepare(<<_End_Of_SQL_); - SELECT login_name - FROM profiles - WHERE groupset=9223372036854775807 -_End_Of_SQL_ -$sth->execute; -# when we have no admin users, prompt for admin email address and password ... -if ($sth->rows == 0) { - my $login = ""; - my $realname = ""; - my $pass1 = ""; - my $pass2 = "*"; - my $admin_ok = 0; - my $admin_create = 1; - my $mailcheckexp = Param('emailregexp'); - my $mailcheck = Param('emailregexpdesc'); - - print "\nLooks like we don't have an administrator set up yet. Either this is your\n"; - print "first time using Bugzilla, or your administrator's privs might have accidently\n"; - print "gotten deleted at some point.\n"; - while(! $admin_ok ) { - while( $login eq "" ) { - print "Enter the e-mail address of the administrator: "; - $login = $answer{'ADMIN_EMAIL'} - || ($silent && die("cant preload ADMIN_EMAIL")) - || ; - chomp $login; - if(! $login ) { - print "\nYou DO want an administrator, don't you?\n"; - } - unless ($login =~ /$mailcheckexp/) { - print "\nThe login address is invalid:\n"; - print "$mailcheck\n"; - print "You can change this test on the params page once checksetup has successfully\n"; - print "completed.\n\n"; - # Go round, and ask them again - $login = ""; - } - } - $login = $dbh->quote($login); - $sth = $dbh->prepare(<<_End_Of_SQL_); - SELECT login_name - FROM profiles - WHERE login_name=$login -_End_Of_SQL_ - $sth->execute; - if ($sth->rows > 0) { - print "$login already has an account.\n"; - print "Make this user the administrator? [Y/n] "; - my $ok = $answer{'ADMIN_OK'} - || ($silent && die("cant preload ADMIN_OK")) - || ; - chomp $ok; - if ($ok !~ /^n/i) { - $admin_ok = 1; - $admin_create = 0; - } else { - print "OK, well, someone has to be the administrator. Try someone else.\n"; - $login = ""; - } - } else { - print "You entered $login. Is this correct? [Y/n] "; - my $ok = $answer{'ADMIN_OK'} - || ($silent && die("cant preload ADMIN_OK")) - || ; - chomp $ok; - if ($ok !~ /^n/i) { - $admin_ok = 1; - } else { - print "That's okay, typos happen. Give it another shot.\n"; - $login = ""; - } - } - } - - if ($admin_create) { - - while( $realname eq "" ) { - print "Enter the real name of the administrator: "; - $realname = $answer{'ADMIN_REALNAME'} - || ($silent && die("cant preload ADMIN_REALNAME")) - || ; - chomp $realname; - if(! $realname ) { - print "\nReally. We need a full name.\n"; - } - } - - # trap a few interrupts so we can fix the echo if we get aborted. - $SIG{HUP} = \&bailout; - $SIG{INT} = \&bailout; - $SIG{QUIT} = \&bailout; - $SIG{TERM} = \&bailout; - - system("stty","-echo"); # disable input echoing - - while( $pass1 ne $pass2 ) { - while( $pass1 eq "" || $pass1 !~ /^[a-zA-Z0-9-_]{3,16}$/ ) { - print "Enter a password for the administrator account: "; - $pass1 = $answer{'ADMIN_PASSWORD'} - || ($silent && die("cant preload ADMIN_PASSWORD")) - || ; - chomp $pass1; - if(! $pass1 ) { - print "\n\nIt's just plain stupid to not have a password. Try again!\n"; - } elsif ( $pass1 !~ /^.{3,16}$/ ) { - print "The password must be 3-16 characters in length."; - } - } - print "\nPlease retype the password to verify: "; - $pass2 = $answer{'ADMIN_PASSWORD'} - || ($silent && die("cant preload ADMIN_PASSWORD")) - || ; - chomp $pass2; - if ($pass1 ne $pass2) { - print "\n\nPasswords don't match. Try again!\n"; - $pass1 = ""; - $pass2 = "*"; - } - } - - # Crypt the administrator's password - my $cryptedpassword = Crypt($pass1); - - system("stty","echo"); # re-enable input echoing - $SIG{HUP} = 'DEFAULT'; # and remove our interrupt hooks - $SIG{INT} = 'DEFAULT'; - $SIG{QUIT} = 'DEFAULT'; - $SIG{TERM} = 'DEFAULT'; - - $realname = $dbh->quote($realname); - $cryptedpassword = $dbh->quote($cryptedpassword); - - $dbh->do(<<_End_Of_SQL_); - INSERT INTO profiles - (login_name, realname, cryptpassword, groupset) - VALUES ($login, $realname, $cryptedpassword, 0x7fffffffffffffff) -_End_Of_SQL_ - } else { - $dbh->do(<<_End_Of_SQL_); - UPDATE profiles - SET groupset=0x7fffffffffffffff - WHERE login_name=$login -_End_Of_SQL_ - } - print "\n$login is now set up as the administrator account.\n"; -} - - - - ########################################################################### # Create initial test product if there are no products present. ########################################################################### - -$sth = $dbh->prepare(<<_End_Of_SQL_); - SELECT userid - FROM profiles - WHERE groupset=9223372036854775807 -_End_Of_SQL_ -$sth->execute; -my ($adminuid) = $sth->fetchrow_array; -if (!$adminuid) { die "No administator!" } # should never get here $sth = $dbh->prepare("SELECT description FROM products"); $sth->execute; unless ($sth->rows) { @@ -2126,19 +1934,20 @@ unless ($sth->rows) { $sth->execute; my ($product_id) = $sth->fetchrow_array; $dbh->do(qq{INSERT INTO versions (value, product_id) VALUES ("other", $product_id)}); + # note: since admin user is not yet known, components gets a 0 for + # initialowner and this is fixed during final checks. $dbh->do("INSERT INTO components (name, product_id, description, initialowner, initialqacontact) VALUES (" . "'TestComponent', $product_id, " . "'This is a test component in the test product database. " . "This ought to be blown away and replaced with real stuff in " . - "a finished installation of bugzilla.', $adminuid, 0)"); + "a finished installation of Bugzilla.', 0, 0)"); $dbh->do(qq{INSERT INTO milestones (product_id, value) VALUES ($product_id,"---")}); } - ########################################################################### # Update the tables to the current definition ########################################################################### @@ -2238,8 +2047,10 @@ sub TableExists ($) # really old fields that were added before checksetup.pl existed # but aren't in very old bugzilla's (like 2.1) # Steve Stock (sstock@iconnect-inc.com) + +# bug 157756 - groupsets replaced by maps +# AddField('bugs', 'groupset', 'bigint not null'); AddField('bugs', 'target_milestone', 'varchar(20) not null default "---"'); -AddField('bugs', 'groupset', 'bigint not null'); AddField('bugs', 'qa_contact', 'mediumint not null'); AddField('bugs', 'status_whiteboard', 'mediumtext not null'); AddField('products', 'disallownew', 'tinyint not null'); @@ -2673,7 +2484,8 @@ if (!GetFieldDef('bugs', 'everconfirmed')) { } AddField('products', 'maxvotesperbug', 'smallint not null default 10000'); AddField('products', 'votestoconfirm', 'smallint not null'); -AddField('profiles', 'blessgroupset', 'bigint not null'); +# bug 157756 - groupsets replaced by maps +# AddField('profiles', 'blessgroupset', 'bigint not null'); # 2000-03-21 Adding a table for target milestones to # database - matthew@zeroknowledge.com @@ -3216,6 +3028,222 @@ if (($fielddef = GetFieldDef("attachments", "creation_ts")) && ChangeFieldType("attachments", "creation_ts", "datetime NOT NULL"); } +# 2002-08-XX - bugreport@peshkin.net - bug 157756 +# +# If the whole groups system is new, but the installation isn't, +# convert all the old groupset groups, etc... +# +# This requires: +# 1) define groups ids in group table +# 2) populate user_group_map with grants from old groupsets and blessgroupsets +# 3) populate bug_group_map with data converted from old bug groupsets +# 4) convert activity logs to use group names instead of numbers +# 5) identify the admin from the old all-ones groupset +# +# ListBits(arg) returns a list of UNKNOWN if the group +# has been deleted for all bits set in arg. When the activity +# records are converted from groupset numbers to lists of +# group names, ListBits is used to fill in a list of references +# to groupset bits for groups that no longer exist. +# +sub ListBits { + my ($num) = @_; + my @res = (); + my $curr = 1; + while (1) { + # Convert a big integer to a list of bits + my $sth = $dbh->prepare("SELECT ($num & ~$curr) > 0, + ($num & $curr), + ($num & ~$curr), + $curr << 1"); + $sth->execute; + my ($more, $thisbit, $remain, $nval) = $sth->fetchrow_array; + push @res,"UNKNOWN<$curr>" if ($thisbit); + $curr = $nval; + $num = $remain; + last if (!$more); + } + return @res; +} + +my @admins = (); +# The groups system needs to be converted if groupset exists +if (GetFieldDef("profiles", "groupset")) { + AddField('groups', 'last_changed', 'datetime not null'); + # Some mysql versions will promote any unique key to primary key + # so all unique keys are removed first and then added back in + $dbh->do("ALTER TABLE groups DROP INDEX bit") if GetIndexDef("groups","bit"); + $dbh->do("ALTER TABLE groups DROP INDEX name") if GetIndexDef("groups","name"); + $dbh->do("ALTER TABLE groups DROP PRIMARY KEY"); + AddField('groups', 'id', 'mediumint not null auto_increment primary key'); + $dbh->do("ALTER TABLE groups ADD UNIQUE (name)"); + AddField('profiles', 'refreshed_when', 'datetime not null'); + + # Convert all existing groupset records to map entries before removing + # groupset fields or removing "bit" from groups. + $sth = $dbh->prepare("SELECT bit, id FROM groups + WHERE bit > 0"); + $sth->execute(); + while (my ($bit, $gid) = $sth->fetchrow_array) { + # Create user_group_map membership grants for old groupsets. + # Get each user with the old groupset bit set + my $sth2 = $dbh->prepare("SELECT userid FROM profiles + WHERE (groupset & $bit) != 0"); + $sth2->execute(); + while (my ($uid) = $sth2->fetchrow_array) { + # Check to see if the user is already a member of the group + # and, if not, insert a new record. + my $query = "SELECT user_id FROM user_group_map + WHERE group_id = $gid AND user_id = $uid + AND isbless = 0"; + my $sth3 = $dbh->prepare($query); + $sth3->execute(); + if ( !$sth3->fetchrow_array() ) { + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES($uid, $gid, 0, 0)"); + } + } + # Create user can bless group grants for old groupsets. + # Get each user with the old blessgroupset bit set + $sth2 = $dbh->prepare("SELECT userid FROM profiles + WHERE (blessgroupset & $bit) != 0"); + $sth2->execute(); + while (my ($uid) = $sth2->fetchrow_array) { + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES($uid, $gid, 1, 0)"); + } + # Create bug_group_map records for old groupsets. + # Get each bug with the old group bit set. + $sth2 = $dbh->prepare("SELECT bug_id FROM bugs + WHERE (groupset & $bit) != 0"); + $sth2->execute(); + while (my ($bug_id) = $sth2->fetchrow_array) { + # Insert the bug, group pair into the bug_group_map. + $dbh->do("INSERT INTO bug_group_map + (bug_id, group_id) + VALUES($bug_id, $gid)"); + } + } + # Replace old activity log groupset records with lists of names of groups. + # Start by defining the bug_group field and getting its id. + AddFDef("bug_group", "Group", 0); + $sth = $dbh->prepare("SELECT fieldid FROM fielddefs WHERE name = " . $dbh->quote('bug_group')); + $sth->execute(); + my ($bgfid) = $sth->fetchrow_array; + # Get the field id for the old groupset field + $sth = $dbh->prepare("SELECT fieldid FROM fielddefs WHERE name = " . $dbh->quote('groupset')); + $sth->execute(); + my ($gsid) = $sth->fetchrow_array; + # Get all bugs_activity records from groupset changes + $sth = $dbh->prepare("SELECT bug_id, bug_when, who, added, removed + FROM bugs_activity WHERE fieldid = $gsid"); + $sth->execute(); + while (my ($bug_id, $bug_when, $who, $added, $removed) = $sth->fetchrow_array) { + $added ||= 0; + $removed ||= 0; + # Get names of groups added. + my $sth2 = $dbh->prepare("SELECT name FROM groups WHERE (bit & $added) != 0 AND (bit & $removed) = 0"); + $sth2->execute(); + my @logadd = (); + while (my ($n) = $sth2->fetchrow_array) { + push @logadd, $n; + } + # Get names of groups removed. + $sth2 = $dbh->prepare("SELECT name FROM groups WHERE (bit & $removed) != 0 AND (bit & $added) = 0"); + $sth2->execute(); + my @logrem = (); + while (my ($n) = $sth2->fetchrow_array) { + push @logrem, $n; + } + # Get list of group bits added that correspond to missing groups. + $sth2 = $dbh->prepare("SELECT ($added & ~BIT_OR(bit)) FROM groups"); + $sth2->execute(); + my ($miss) = $sth2->fetchrow_array; + if ($miss) { + push @logadd, ListBits($miss); + print "\nWARNING - GROUPSET ACTIVITY ON BUG $bug_id CONTAINS DELETED GROUPS\n"; + } + # Get list of group bits deleted that correspond to missing groups. + $sth2 = $dbh->prepare("SELECT ($removed & ~BIT_OR(bit)) FROM groups"); + $sth2->execute(); + ($miss) = $sth2->fetchrow_array; + if ($miss) { + push @logrem, ListBits($miss); + print "\nWARNING - GROUPSET ACTIVITY ON BUG $bug_id CONTAINS DELETED GROUPS\n"; + } + my $logr = ""; + my $loga = ""; + $logr = join(", ", @logrem) . '?' if @logrem; + $loga = join(", ", @logadd) . '?' if @logadd; + # Replace to old activity record with the converted data. + $dbh->do("UPDATE bugs_activity SET fieldid = $bgfid, added = " . + $dbh->quote($loga) . ", removed = " . + $dbh->quote($logr) . + " WHERE bug_id = $bug_id AND bug_when = " . $dbh->quote($bug_when) . + " AND who = $who AND fieldid = $gsid"); + + } + # Replace groupset changes with group name changes in profiles_activity. + # Get profiles_activity records for groupset. + $sth = $dbh->prepare("SELECT userid, profiles_when, who, newvalue, oldvalue + FROM profiles_activity WHERE fieldid = $gsid"); + $sth->execute(); + while (my ($uid, $uwhen, $uwho, $added, $removed) = $sth->fetchrow_array) { + $added ||= 0; + $removed ||= 0; + # Get names of groups added. + my $sth2 = $dbh->prepare("SELECT name FROM groups WHERE (bit & $added) != 0 AND (bit & $removed) = 0"); + $sth2->execute(); + my @logadd = (); + while (my ($n) = $sth2->fetchrow_array) { + push @logadd, $n; + } + # Get names of groups removed. + $sth2 = $dbh->prepare("SELECT name FROM groups WHERE (bit & $removed) != 0 AND (bit & $added) = 0"); + $sth2->execute(); + my @logrem = (); + while (my ($n) = $sth2->fetchrow_array) { + push @logrem, $n; + } + my $ladd = ""; + my $lrem = ""; + $ladd = join(", ", @logadd) . '?' if @logadd; + $lrem = join(", ", @logrem) . '?' if @logrem; + # Replace profiles_activity record for groupset change with group list. + $dbh->do("UPDATE profiles_activity SET fieldid = $bgfid, newvalue = " . + $dbh->quote($ladd) . ", oldvalue = " . + $dbh->quote($lrem) . + " WHERE userid = $uid AND profiles_when = " . + $dbh->quote($uwhen) . + " AND who = $uwho AND fieldid = $gsid"); + + } + + # Identify admin group. + my $sth = $dbh->prepare("SELECT id FROM groups + WHERE name = 'admin'"); + $sth->execute(); + my ($adminid) = $sth->fetchrow_array(); + # find existing admins + # Don't lose admins from DBs where Bug 157704 applies + $sth = $dbh->prepare("SELECT userid, (groupset & 65536), login_name FROM profiles + WHERE (groupset | 65536) = 9223372036854775807"); + $sth->execute(); + while ( my ($userid, $iscomplete, $login_name) = $sth->fetchrow_array() ) { + # existing administrators are made members of group "admin" + print "\nWARNING - $login_name IS AN ADMIN IN SPITE OF BUG 157704\n\n" + if (!$iscomplete); + push @admins, $userid; + } + DropField('profiles','groupset'); + DropField('profiles','blessgroupset'); + DropField('bugs','groupset'); + DropField('groups','bit'); + $dbh->do("DELETE FROM fielddefs WHERE name = " . $dbh->quote('groupset')); +} + # If you had to change the --TABLE-- definition in any way, then add your # differential change code *** A B O V E *** this comment. # @@ -3225,9 +3253,291 @@ if (($fielddef = GetFieldDef("attachments", "creation_ts")) && # AddField/DropField/ChangeFieldType/RenameField code above. This would then # be honored by everyone who updates his Bugzilla installation. # + +# +# BugZilla uses --GROUPS-- to assign various rights to its users. +# + +AddGroup('tweakparams', 'Can tweak operating parameters'); +AddGroup('editusers', 'Can edit or disable users'); +AddGroup('creategroups', 'Can create and destroy groups.'); +AddGroup('editcomponents', 'Can create, destroy, and edit components.'); +AddGroup('editkeywords', 'Can create, destroy, and edit keywords.'); +AddGroup('admin', 'Administrators'); + + +if (!GroupDoesExist("editbugs")) { + my $id = AddGroup('editbugs', 'Can edit all aspects of any bug.', ".*"); + my $sth = $dbh->prepare("SELECT userid FROM profiles"); + $sth->execute(); + while (my ($userid) = $sth->fetchrow_array()) { + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($userid, $id, 0, 0)"); + } +} + +if (!GroupDoesExist("canconfirm")) { + my $id = AddGroup('canconfirm', 'Can confirm a bug.', ".*"); + my $sth = $dbh->prepare("SELECT userid FROM profiles"); + $sth->execute(); + while (my ($userid) = $sth->fetchrow_array()) { + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($userid, $id, 0, 0)"); + } + +} + + +########################################################################### +# Create Administrator --ADMIN-- +########################################################################### + + +sub bailout { # this is just in case we get interrupted while getting passwd + system("stty","echo"); # re-enable input echoing + exit 1; +} + +if (@admins) { + # Identify admin group. + my $sth = $dbh->prepare("SELECT id FROM groups + WHERE name = 'admin'"); + $sth->execute(); + my ($adminid) = $sth->fetchrow_array(); + foreach my $userid (@admins) { + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($userid, $adminid, 0, 0)"); + # Existing administrators are made blessers of group "admin" + # but only explitly defined blessers can bless group admin. + # Other groups can be blessed by any admin (by default) or additional + # defined blessers. + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($userid, $adminid, 1, 0)"); + } + $sth = $dbh->prepare("SELECT id FROM groups"); + $sth->execute(); + while ( my ($id) = $sth->fetchrow_array() ) { + # Admins can bless every group. + $dbh->do("INSERT INTO group_group_map + (member_id, grantor_id, isbless) + VALUES ($adminid, $id, 1)"); + # Admins are initially members of every group. + next if ($id == $adminid); + $dbh->do("INSERT INTO group_group_map + (member_id, grantor_id, isbless) + VALUES ($adminid, $id, 0)"); + } +} + + +my @groups = (); +$sth = $dbh->prepare("select id from groups"); +$sth->execute(); +while ( my @row = $sth->fetchrow_array() ) { + push (@groups, $row[0]); +} + +# Prompt the user for the email address and name of an administrator. Create +# that login, if it doesn't exist already, and make it a member of all groups. + +$sth = $dbh->prepare("SELECT user_id FROM groups, user_group_map" . + " WHERE name = 'admin' AND id = group_id"); +$sth->execute; +# when we have no admin users, prompt for admin email address and password ... +if ($sth->rows == 0) { + my $login = ""; + my $realname = ""; + my $pass1 = ""; + my $pass2 = "*"; + my $admin_ok = 0; + my $admin_create = 1; + my $mailcheckexp = ""; + my $mailcheck = ""; + + # Here we look to see what the emailregexp is set to so we can + # check the email addy they enter. Bug 96675. If they have no + # params (likely but not always the case), we use the default. + if (-e "data/params") { + require "data/params"; # if they have a params file, use that + } + if (Param('emailregexp')) { + $mailcheckexp = Param('emailregexp'); + $mailcheck = Param('emailregexpdesc'); + } else { + $mailcheckexp = '^[^@]+@[^@]+\\.[^@]+$'; + $mailcheck = 'A legal address must contain exactly one \'@\', + and at least one \'.\' after the @.'; + } + + print "\nLooks like we don't have an administrator set up yet. Either this is your\n"; + print "first time using Bugzilla, or your administrator's privileges might have accidently\n"; + print "been deleted.\n"; + while(! $admin_ok ) { + while( $login eq "" ) { + print "Enter the e-mail address of the administrator: "; + $login = $answer{'ADMIN_EMAIL'} + || ($silent && die("cant preload ADMIN_EMAIL")) + || ; + chomp $login; + if(! $login ) { + print "\nYou DO want an administrator, don't you?\n"; + } + unless ($login =~ /$mailcheckexp/) { + print "\nThe login address is invalid:\n"; + print "$mailcheck\n"; + print "You can change this test on the params page once checksetup has successfully\n"; + print "completed.\n\n"; + # Go round, and ask them again + $login = ""; + } + } + $login = $dbh->quote($login); + $sth = $dbh->prepare("SELECT login_name FROM profiles" . + " WHERE login_name=$login"); + $sth->execute; + if ($sth->rows > 0) { + print "$login already has an account.\n"; + print "Make this user the administrator? [Y/n] "; + my $ok = $answer{'ADMIN_OK'} + || ($silent && die("cant preload ADMIN_OK")) + || ; + chomp $ok; + if ($ok !~ /^n/i) { + $admin_ok = 1; + $admin_create = 0; + } else { + print "OK, well, someone has to be the administrator. Try someone else.\n"; + $login = ""; + } + } else { + print "You entered $login. Is this correct? [Y/n] "; + my $ok = $answer{'ADMIN_OK'} + || ($silent && die("cant preload ADMIN_OK")) + || ; + chomp $ok; + if ($ok !~ /^n/i) { + $admin_ok = 1; + } else { + print "That's okay, typos happen. Give it another shot.\n"; + $login = ""; + } + } + } + + if ($admin_create) { + + while( $realname eq "" ) { + print "Enter the real name of the administrator: "; + $realname = $answer{'ADMIN_REALNAME'} + || ($silent && die("cant preload ADMIN_REALNAME")) + || ; + chomp $realname; + if(! $realname ) { + print "\nReally. We need a full name.\n"; + } + } + + # trap a few interrupts so we can fix the echo if we get aborted. + $SIG{HUP} = \&bailout; + $SIG{INT} = \&bailout; + $SIG{QUIT} = \&bailout; + $SIG{TERM} = \&bailout; + + system("stty","-echo"); # disable input echoing + + while( $pass1 ne $pass2 ) { + while( $pass1 eq "" || $pass1 !~ /^[a-zA-Z0-9-_]{3,16}$/ ) { + print "Enter a password for the administrator account: "; + $pass1 = $answer{'ADMIN_PASSWORD'} + || ($silent && die("cant preload ADMIN_PASSWORD")) + || ; + chomp $pass1; + if(! $pass1 ) { + print "\n\nIt's just plain stupid to not have a password. Try again!\n"; + } elsif ( $pass1 !~ /^.{3,16}$/ ) { + print "The password must be 3-16 characters in length."; + } + } + print "\nPlease retype the password to verify: "; + $pass2 = $answer{'ADMIN_PASSWORD'} + || ($silent && die("cant preload ADMIN_PASSWORD")) + || ; + chomp $pass2; + if ($pass1 ne $pass2) { + print "\n\nPasswords don't match. Try again!\n"; + $pass1 = ""; + $pass2 = "*"; + } + } + + # Crypt the administrator's password + my $cryptedpassword = Crypt($pass1); + + system("stty","echo"); # re-enable input echoing + $SIG{HUP} = 'DEFAULT'; # and remove our interrupt hooks + $SIG{INT} = 'DEFAULT'; + $SIG{QUIT} = 'DEFAULT'; + $SIG{TERM} = 'DEFAULT'; + + $realname = $dbh->quote($realname); + $cryptedpassword = $dbh->quote($cryptedpassword); + + $dbh->do("INSERT INTO profiles (login_name, realname, cryptpassword)" . + " VALUES ($login, $realname, $cryptedpassword)"); + } + # Put the admin in each group if not already + my $query = "select userid from profiles where login_name = $login"; + $sth = $dbh->prepare($query); + $sth->execute(); + my ($userid) = $sth->fetchrow_array(); + + foreach my $group (@groups) { + my $query = "SELECT user_id FROM user_group_map + WHERE group_id = $group AND user_id = $userid + AND isbless = 0"; + $sth = $dbh->prepare($query); + $sth->execute(); + if ( !$sth->fetchrow_array() ) { + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($userid, $group, 0, 0)"); + } + } + # the admin also gets an explicit bless capability for the admin group + my $sth = $dbh->prepare("SELECT id FROM groups + WHERE name = 'admin'"); + $sth->execute(); + my ($id) = $sth->fetchrow_array(); + $dbh->do("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($userid, $id, 1, 0)"); + foreach my $group ( @groups ) { + $dbh->do("INSERT INTO group_group_map + (member_id, grantor_id, isbless) + VALUES ($id, $group, 1)"); + } + + print "\n$login is now set up as an administrator account.\n"; +} + + + # # Final checks... +$sth = $dbh->prepare("SELECT user_id FROM groups, user_group_map" . + " WHERE groups.name = 'admin'" . + " AND groups.id = user_group_map.group_id"); +$sth->execute; +my ($adminuid) = $sth->fetchrow_array; +if (!$adminuid) { die "No administrator!" } # should never get here +# when test product was created, admin was unknown +$dbh->do("UPDATE components SET initialowner = $adminuid WHERE initialowner = 0"); + unlink "data/versioncache"; print "Reminder: Bugzilla now requires version 8.7 or later of sendmail.\n" unless $silent; diff --git a/contrib/bug_email.pl b/contrib/bug_email.pl index fb2bbec4d..bf442502c 100755 --- a/contrib/bug_email.pl +++ b/contrib/bug_email.pl @@ -37,7 +37,7 @@ # # You need to work with bug_email.pl the MIME::Parser installed. # -# $Id: bug_email.pl,v 1.13 2002/08/26 06:17:21 bbaetz%student.usyd.edu.au Exp $ +# $Id: bug_email.pl,v 1.14 2002/09/22 17:15:03 bugreport%peshkin.net Exp $ ############################################################### # 02/12/2000 (SML) diff --git a/docs/sgml/administration.sgml b/docs/sgml/administration.sgml index a82a659bf..a0ff3e174 100644 --- a/docs/sgml/administration.sgml +++ b/docs/sgml/administration.sgml @@ -212,33 +212,11 @@ you for this username and password. - If you wish to add more administrative users, you must use the - MySQL interface. Run "mysql" from the command line, and use these - commands: - - - mysql> - use bugs; - - - - mysql> - - - update profiles set groupset=0x7ffffffffffffff where login_name = - "(user's login name)"; - - - + If you wish to add more administrative users, add them to + the "admin" group and, optionally, add edit the tweakparams, editusers, + creategroups, editcomponents, and editkeywords groups to add the + entire admin group to those groups. - - Yes, that is - fourteen - - f - - 's. A whole lot of f-ing going on if you want to create a new - administator. @@ -698,10 +676,22 @@ - Fill out the "New Name", "New Description", and - "New User RegExp" fields. "New User RegExp" allows you to automatically + Fill out the "Group", "Description", and + "User RegExp" fields. "New User RegExp" allows you to automatically place all users who fulfill the Regular Expression into the new group. When you have finished, click "Add". + + The User Regexp is a perl regexp and, if not anchored, will match + any part of an address. So, if you do not want to grant access + into 'mycompany.com' to 'badperson@mycompany.com.hacker.net', use + '@mycompany\.com$' as the regexp. + + + + After you add your new group, edit the new group. On the + edit page, you can specify other groups that should be included + in this group and which groups should be permitted to add and delete + users from this group. @@ -712,17 +702,6 @@ Turn on "usebuggroups" and "usebuggroupsentry" in the "Edit Parameters" screen. - - XXX is this still true? - "usebuggroupsentry" has the capacity to prevent the - administrative user from directly altering bugs because of - conflicting group permissions. If you plan on using - "usebuggroupsentry", you should plan on restricting - administrative account usage to administrative duties only. In - other words, manage bugs with an unpriveleged user account, and - manage users, groups, Products, etc. with the administrative - account. - @@ -734,13 +713,6 @@ - - Bugzilla currently has a limit of 64 groups per installation. If - you have more than about 50 products, you should consider - running multiple Bugzillas. Ask in the newsgroup for other - suggestions for working around this restriction. - - Note that group permissions are such that you need to be a member of all the groups a bug is in, for whatever diff --git a/docs/xml/administration.xml b/docs/xml/administration.xml index a82a659bf..a0ff3e174 100644 --- a/docs/xml/administration.xml +++ b/docs/xml/administration.xml @@ -212,33 +212,11 @@ you for this username and password. - If you wish to add more administrative users, you must use the - MySQL interface. Run "mysql" from the command line, and use these - commands: - - - mysql> - use bugs; - - - - mysql> - - - update profiles set groupset=0x7ffffffffffffff where login_name = - "(user's login name)"; - - - + If you wish to add more administrative users, add them to + the "admin" group and, optionally, add edit the tweakparams, editusers, + creategroups, editcomponents, and editkeywords groups to add the + entire admin group to those groups. - - Yes, that is - fourteen - - f - - 's. A whole lot of f-ing going on if you want to create a new - administator. @@ -698,10 +676,22 @@ - Fill out the "New Name", "New Description", and - "New User RegExp" fields. "New User RegExp" allows you to automatically + Fill out the "Group", "Description", and + "User RegExp" fields. "New User RegExp" allows you to automatically place all users who fulfill the Regular Expression into the new group. When you have finished, click "Add". + + The User Regexp is a perl regexp and, if not anchored, will match + any part of an address. So, if you do not want to grant access + into 'mycompany.com' to 'badperson@mycompany.com.hacker.net', use + '@mycompany\.com$' as the regexp. + + + + After you add your new group, edit the new group. On the + edit page, you can specify other groups that should be included + in this group and which groups should be permitted to add and delete + users from this group. @@ -712,17 +702,6 @@ Turn on "usebuggroups" and "usebuggroupsentry" in the "Edit Parameters" screen. - - XXX is this still true? - "usebuggroupsentry" has the capacity to prevent the - administrative user from directly altering bugs because of - conflicting group permissions. If you plan on using - "usebuggroupsentry", you should plan on restricting - administrative account usage to administrative duties only. In - other words, manage bugs with an unpriveleged user account, and - manage users, groups, Products, etc. with the administrative - account. - @@ -734,13 +713,6 @@ - - Bugzilla currently has a limit of 64 groups per installation. If - you have more than about 50 products, you should consider - running multiple Bugzillas. Ask in the newsgroup for other - suggestions for working around this restriction. - - Note that group permissions are such that you need to be a member of all the groups a bug is in, for whatever diff --git a/duplicates.cgi b/duplicates.cgi index 3eeab3fb5..0e27f077e 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -40,7 +40,7 @@ GetVersionTable(); quietly_check_login(); -use vars qw (%FORM $userid $usergroupset @legal_product); +use vars qw (%FORM $userid @legal_product); my %dbmcount; my %count; @@ -160,9 +160,7 @@ if (scalar(%count)) { # Limit to a single product if requested $query .= (" AND bugs.product_id = " . $product_id) if $product_id; - SendSQL(SelectVisible($query, - $userid, - $usergroupset)); + SendSQL($query); while (MoreSQLData()) { # Note: maximum row count is dealt with in the template. @@ -170,6 +168,7 @@ if (scalar(%count)) { my ($id, $component, $bug_severity, $op_sys, $target_milestone, $short_desc, $bug_status, $resolution) = FetchSQLData(); + next if (!CanSeeBug($id, $::userid)); # Limit to open bugs only if requested next if $openonly && ($resolution ne ""); diff --git a/editgroups.cgi b/editgroups.cgi index 5bcd4f61c..9ecda4138 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -19,6 +19,7 @@ # Rights Reserved. # # Contributor(s): Dave Miller +# Joel Peshkin # Jacob Steenhagen # Code derived from editowners.cgi and editusers.cgi @@ -99,36 +100,36 @@ sub PutTrailer (@) unless ($action) { PutHeader("Edit Groups","Edit Groups","This lets you edit the groups available to put users in."); - print "
\n"; print "
Attachment TypeModifiedCreated Status Actions
Privacy: + If the attachment is private, check the box below.
+ + +
Obsoletes: (optional) Check each existing attachment made obsolete by your new attachment.
[% IF attachments.size %] [% FOREACH attachment = attachments %] - - [% attachment.id %]: [% attachment.description FILTER html %]
+ [% attachment.id %]: [% attachment.description FILTER html %]
+ [% END %] [% END %] [% ELSE %] [no attachments can be made obsolete] diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 5d01d2898..c9bc7c1cd 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -163,6 +163,9 @@
+ [% IF (Param("insidergroup") && UserInGroup(Param("insidergroup"))) %] + private

+ [% END %] [% IF statusdefs.size %] Status:
diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index bc25c5721..e7aa8b0ef 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -28,9 +28,10 @@
Status Actions
[% IF attachment.isobsolete %] [% attachment.description FILTER html %] @@ -67,6 +68,7 @@ [% END %]
\n"; print ""; - print ""; print ""; print ""; print ""; - print ""; + print ""; + print ""; print ""; print "\n"; - SendSQL("SELECT bit,name,description,userregexp,isactive " . + SendSQL("SELECT id,name,description,userregexp,isactive,isbuggroup " . "FROM groups " . - "WHERE isbuggroup != 0 " . - "ORDER BY bit"); + "ORDER BY isbuggroup, name"); while (MoreSQLData()) { - my ($bit, $name, $desc, $regexp, $isactive) = FetchSQLData(); + my ($groupid, $name, $desc, $regexp, $isactive, $isbuggroup) = FetchSQLData(); print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; } print "\n"; @@ -136,62 +137,135 @@ unless ($action) { print "\n"; print "\n"; print "
BitNameDescriptionUser RegExpActiveUse For BugsTypeAction
$bit\n"; - print "\n"; - print "\n"; - print "\n"; - print "Delete
$name$desc$regexp "; + print "X" if $isactive; + print "    "; + print (($isbuggroup == 0 ) ? "system" : "user"); + print "  + Edit"; + print " | Delete" if ($isbuggroup != 0); + print "
Add Group
\n"; - print ""; - print "\n"; - print "

"; print "Name is what is used with the UserInGroup() function in any customized cgi files you write that use a given group. It can also be used by -people submitting bugs by email to limit a bug to a certain groupset.

"; +people submitting bugs by email to limit a bug to a certain set of groups.

"; print "Description is what will be shown in the bug reports to members of the group where they can choose whether the bug will be restricted to others in the same group.

"; print "User RegExp is optional, and if filled in, will automatically grant membership to this group to anyone creating a new account with an -email address that matches this regular expression.

"; - print "The Active flag determines whether or not the group is active. -If you deactivate a group it will no longer be possible for users to add bugs -to that group, although bugs already in the group will remain in the group. -Deactivating a group is a much less drastic way to stop a group from growing -than deleting the group would be.

"; - print "In addition, the following groups that determine user privileges -exist. You can only edit the User rexexp on these groups. You should also take -care not to duplicate the Names of any of them in your user groups.

"; - print "Also please note that both of the Submit Changes buttons on this page -will submit the changes in both tables. There are two buttons simply for the -sake of convience.

"; +email address that matches this perl regular expression. Do not forget the trailing \'\$\'. Example \'\@mycompany\\.com\$\'

"; + print "The Use For Bugs flag determines whether or not the group is eligible to be used for bugs. +If you remove this flag, it will no longer be possible for users to add bugs +to this group, although bugs already in the group will remain in the group. +Doing so is a much less drastic way to stop a group from growing +than deleting the group as well as a way to maintain lists of users without cluttering the lists of groups used for bug restrictions.

"; + print "The Type field identifies system groups.

"; - print "\n"; - print ""; - print ""; - print ""; - print ""; - print ""; - print "\n"; + PutFooter(); + exit; +} - SendSQL("SELECT bit,name,description,userregexp " . - "FROM groups " . - "WHERE isbuggroup = 0 " . - "ORDER BY bit"); +# +# +# action='changeform' -> present form for altering an existing group +# +# (next action will be 'postchanges') +# + +if ($action eq 'changeform') { + PutHeader("Change Group"); + + my $gid = trim($::FORM{group} || ''); + unless ($gid) { + ShowError("No group specified.
" . + "Click the Back button and try again."); + PutFooter(); + exit; + } + + SendSQL("SELECT id, name, description, userregexp, isactive, isbuggroup + FROM groups WHERE id=" . SqlQuote($gid)); + my ($group_id, $name, $description, $rexp, $isactive, $isbuggroup) + = FetchSQLData(); + + print "\n"; + print "
BitNameDescriptionUser RegExp
"; + print " + "; + if ($isbuggroup == 1) { + print " + "; + } + print "
Group:"; + if ($isbuggroup == 0) { + print "$name"; + } else { + print " + "; + } + print "
Description:"; + if ($isbuggroup == 0) { + print "$description"; + } else { + print " + "; + } + print "
User Regexp:"; + print " +
Use For Bugs: + + +
+
+ Users become members of this group in one of three ways: +
+ - by being explicity included when the user is edited +
+ - by matching the user regexp above +
+ - by being a member of one of the groups included in this group + by checking the boxes + below.

\n"; + + print ""; + print ""; + print ""; + print ""; + + # For each group, we use left joins to establish the existance of + # a record making that group a member of this group + # and the existance of a record permitting that group to bless + # this one + SendSQL("SELECT groups.id, groups.name, groups.description," . + " group_group_map.member_id IS NOT NULL," . + " B.member_id IS NOT NULL" . + " FROM groups" . + " LEFT JOIN group_group_map" . + " ON group_group_map.member_id = groups.id" . + " AND group_group_map.grantor_id = $group_id" . + " AND group_group_map.isbless = 0" . + " LEFT JOIN group_group_map as B" . + " ON B.member_id = groups.id" . + " AND B.grantor_id = $group_id" . + " AND B.isbless" . + " WHERE groups.id != $group_id ORDER by name"); while (MoreSQLData()) { - my ($bit, $name, $desc, $regexp) = FetchSQLData(); - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; - print "\n"; + my ($grpid, $grpnam, $grpdesc, $grpmember, $blessmember) = FetchSQLData(); + my $grpchecked = $grpmember ? "CHECKED" : ""; + my $blesschecked = $blessmember ? "CHECKED" : ""; + print ""; + print ""; + print ""; + print ""; + print ""; + print "\n"; } - print "
Members of these groups can grant membership to this group
|Members of these groups are included in this group
||
$bit$name$desc
"; + print ""; + print "$grpnam$grpdesc

\n"; - print "\n"; - print "\n"; + print "


"; + print "\n"; + print "\n"; + print "\n"; + print ""; - PutFooter(); + + + PutTrailer("Back to group list"); exit; } @@ -209,7 +283,7 @@ if ($action eq 'add') { print "
New NameNew DescriptionNew User RegExpActiveUse For Bugs
\n"; print ""; - print ""; + print ""; print ""; print ""; print "\n"; print "\n"; - print "\n"; + print "\n"; print "\n"; print "\n"; print "\n"; @@ -390,26 +432,26 @@ if ($action eq 'del') { print "\n"; my $cantdelete = 0; - SendSQL("SELECT login_name FROM profiles WHERE " . - "(groupset & $bit) OR (blessgroupset & $bit)"); + SendSQL("SELECT user_id FROM user_group_map + WHERE group_id = $gid AND isbless = 0"); if (!FetchOneColumn()) {} else { $cantdelete = 1; print " One or more users belong to this group. You cannot delete this group while there are users in it.
-Show me which users. - Remove all users from +Show me which users. - Remove all users from this group for me

"; } - SendSQL("SELECT bug_id FROM bugs WHERE (groupset & $bit)"); + SendSQL("SELECT bug_id FROM bug_group_map WHERE group_id = $gid"); + my $buglist=""; if (MoreSQLData()) { - $cantdelete = 1; - my $buglist = "0"; - while (MoreSQLData()) { - my ($bug) = FetchSQLData(); - $buglist .= "," . $bug; - } + $cantdelete = 1; + my $buglist = "0"; + while (MoreSQLData()) { + my ($bug) = FetchSQLData(); + $buglist .= "," . $bug; + } print " One or more bug reports are visible only to this group. You cannot delete this group while any bugs are using it.
@@ -440,7 +482,7 @@ You cannot delete this group while it is tied to a product.
} print "

\n"; print "\n"; - print "\n"; + print "\n"; print ""; PutTrailer("No, go back to the group list"); @@ -453,8 +495,8 @@ You cannot delete this group while it is tied to a product.
if ($action eq 'delete') { PutHeader("Deleting group"); - my $bit = trim($::FORM{group} || ''); - unless ($bit) { + my $gid = trim($::FORM{group} || ''); + unless ($gid) { ShowError("No group specified.
" . "Click the Back button and try again."); PutFooter(); @@ -462,27 +504,19 @@ if ($action eq 'delete') { } SendSQL("SELECT name " . "FROM groups " . - "WHERE bit = " . SqlQuote($bit)); + "WHERE group_id = " . SqlQuote($gid)); my ($name) = FetchSQLData(); my $cantdelete = 0; - my $opblessgroupset = '9223372036854775807'; # This is all 64 bits. - SendSQL("SELECT userid FROM profiles " . - "WHERE (groupset & $opblessgroupset)=$opblessgroupset"); - my @opusers = (); - while (MoreSQLData()) { - my ($userid) = FetchSQLData(); - push @opusers, $userid; # cache a list of the users with admin powers - } - SendSQL("SELECT login_name FROM profiles WHERE " . - "(groupset & $bit)=$bit OR (blessgroupset & $bit)=$bit"); + SendSQL("SELECT user_id FROM user_group_map + WHERE group_id = $gid AND isbless = 0"); if (FetchOneColumn()) { if (!defined $::FORM{'removeusers'}) { $cantdelete = 1; } } - SendSQL("SELECT bug_id FROM bugs WHERE (groupset & $bit)=$bit"); + SendSQL("SELECT bug_id FROM bug_group_map WHERE group_id = $gid"); if (FetchOneColumn()) { if (!defined $::FORM{'removebugs'}) { $cantdelete = 1; @@ -496,141 +530,123 @@ if ($action eq 'delete') { } if ($cantdelete == 1) { - ShowError("This group cannot be deleted because there are child " . - "records in the database which refer to it. All child records " . + ShowError("This group cannot be deleted because there are " . + "records in the database which refer to it. All such records " . "must be removed or altered to remove the reference to this " . "group before the group can be deleted."); - print "" . + print "" . "View the list of which records are affected
"; PutTrailer("Back to group list"); exit; } - SendSQL("SELECT login_name,groupset,blessgroupset FROM profiles WHERE " . - "(groupset & $bit) OR (blessgroupset & $bit)"); - if (FetchOneColumn()) { - SendSQL("UPDATE profiles SET groupset=(groupset-$bit) " . - "WHERE (groupset & $bit)"); - print "All users have been removed from group $bit.
"; - SendSQL("UPDATE profiles SET blessgroupset=(blessgroupset-$bit) " . - "WHERE (blessgroupset & $bit)"); - print "All users with authority to add users to group $bit have " . - "had that authority removed.
"; - } - SendSQL("SELECT bug_id FROM bugs WHERE (groupset & $bit)"); - if (FetchOneColumn()) { - SendSQL("UPDATE bugs SET groupset=(groupset-$bit), delta_ts=delta_ts " . - "WHERE (groupset & $bit)"); - print "All bugs have had group bit $bit cleared. Any of these " . - "bugs that were not also in another group are now " . - "publicly visible.
"; - } - SendSQL("DELETE FROM groups WHERE bit=$bit"); - print "Group $bit has been deleted.
"; - - foreach my $userid (@opusers) { - SendSQL("UPDATE profiles SET groupset=$opblessgroupset " . - "WHERE userid=$userid"); - print "Group bits restored for " . DBID_to_name($userid) . - " (maintainer)
\n"; - } + SendSQL("DELETE FROM user_group_map WHERE group_id = $gid"); + SendSQL("DELETE FROM group_group_map WHERE grantor_id = $gid"); + SendSQL("DELETE FROM bug_group_map WHERE group_id = $gid"); + SendSQL("DELETE FROM groups WHERE id = $gid"); + print "Group $gid has been deleted.
"; + PutTrailer("Back to group list"); exit; } # -# action='update' -> update the groups +# action='postchanges' -> update the groups # -if ($action eq 'update') { - PutHeader("Updating groups"); - +if ($action eq 'postchanges') { + PutHeader("Updating group hierarchy"); + my $gid = trim($::FORM{group} || ''); + unless ($gid) { + ShowError("No group specified.
" . + "Click the Back button and try again."); + PutFooter(); + exit; + } + SendSQL("SELECT isbuggroup FROM groups WHERE id = $gid"); + my ($isbuggroup) = FetchSQLData(); my $chgs = 0; - - foreach my $b (grep(/^name-\d*$/, keys %::FORM)) { - if ($::FORM{$b}) { - my $v = substr($b, 5); - -# print "Old: '" . $::FORM{"oldname-$v"} . "', '" . $::FORM{"olddesc-$v"} . -# "', '" . $::FORM{"oldregexp-$v"} . "'
"; -# print "New: '" . $::FORM{"name-$v"} . "', '" . $::FORM{"desc-$v"} . -# "', '" . $::FORM{"regexp-$v"} . "'
"; - - if ($::FORM{"oldname-$v"} ne $::FORM{"name-$v"}) { - $chgs = 1; - SendSQL("SELECT name FROM groups WHERE name=" . - SqlQuote($::FORM{"name-$v"})); - if (!FetchOneColumn()) { - SendSQL("SELECT name FROM groups WHERE name=" . - SqlQuote($::FORM{"oldname-$v"}) . - " && isbuggroup = 0"); - if (FetchOneColumn()) { - ShowError("You cannot update the name of a " . - "system group. Skipping $v"); - } else { - SendSQL("UPDATE groups SET name=" . - SqlQuote($::FORM{"name-$v"}) . - " WHERE bit=" . SqlQuote($v)); - print "Group $v name updated.
\n"; - } - } else { - ShowError("Duplicate name '" . $::FORM{"name-$v"} . - "' specified for group $v.
" . - "Update of group $v name skipped."); - } - } - if ($::FORM{"olddesc-$v"} ne $::FORM{"desc-$v"}) { + if (($isbuggroup == 1) && ($::FORM{"oldname"} ne $::FORM{"name"})) { + $chgs = 1; + SendSQL("UPDATE groups SET name = " . + SqlQuote($::FORM{"name"}) . " WHERE id = $gid"); + } + if (($isbuggroup == 1) && ($::FORM{"olddesc"} ne $::FORM{"desc"})) { + $chgs = 1; + SendSQL("UPDATE groups SET description = " . + SqlQuote($::FORM{"desc"}) . " WHERE id = $gid"); + } + if ($::FORM{"oldrexp"} ne $::FORM{"rexp"}) { + $chgs = 1; + if (!eval {qr/$::FORM{"rexp"}/}) { + ShowError("The regular expression you entered is invalid. " . + "Please click the Back button and try again."); + PutFooter(); + exit; + } + SendSQL("UPDATE groups SET userregexp = " . + SqlQuote($::FORM{"rexp"}) . " WHERE id = $gid"); + } + if (($isbuggroup == 1) && ($::FORM{"oldisactive"} ne $::FORM{"isactive"})) { + $chgs = 1; + SendSQL("UPDATE groups SET isactive = " . + SqlQuote($::FORM{"isactive"}) . " WHERE id = $gid"); + } + + print "Checking...."; + foreach my $b (grep(/^oldgrp-\d*$/, keys %::FORM)) { + if (defined($::FORM{$b})) { + my $v = substr($b, 7); + my $grp = $::FORM{"grp-$v"} || 0; + if ($::FORM{"oldgrp-$v"} != $grp) { $chgs = 1; - SendSQL("SELECT description FROM groups WHERE description=" . - SqlQuote($::FORM{"desc-$v"})); - if (!FetchOneColumn()) { - SendSQL("UPDATE groups SET description=" . - SqlQuote($::FORM{"desc-$v"}) . - " WHERE bit=" . SqlQuote($v)); - print "Group $v description updated.
\n"; + print "changed"; + if ($grp != 0) { + print " set "; + SendSQL("INSERT INTO group_group_map + (member_id, grantor_id, isbless) + VALUES ($v, $gid, 0)"); } else { - ShowError("Duplicate description '" . $::FORM{"desc-$v"} . - "' specified for group $v.
" . - "Update of group $v description skipped."); + print " cleared "; + SendSQL("DELETE FROM group_group_map + WHERE member_id = $v AND grantor_id = $gid + AND isbless = 0"); } } - if ($::FORM{"oldregexp-$v"} ne $::FORM{"regexp-$v"}) { - $chgs = 1; - SendSQL("UPDATE groups SET userregexp=" . - SqlQuote($::FORM{"regexp-$v"}) . - " WHERE bit=" . SqlQuote($v)); - print "Group $v user regexp updated.
\n"; - } - # convert an undefined value in the inactive field to zero - # (this occurs when the inactive checkbox is not checked - # and the browser does not send the field to the server) - my $isactive = $::FORM{"isactive-$v"} || 0; - if ($::FORM{"oldisactive-$v"} != $isactive) { + + my $bless = $::FORM{"bless-$v"} || 0; + if ($::FORM{"oldbless-$v"} != $bless) { $chgs = 1; - if ($isactive == 0 || $isactive == 1) { - SendSQL("UPDATE groups SET isactive=$isactive" . - " WHERE bit=" . SqlQuote($v)); - print "Group $v active flag updated.
\n"; + print "changed"; + if ($bless != 0) { + print " set "; + SendSQL("INSERT INTO group_group_map + (member_id, grantor_id, isbless) + VALUES ($v, $gid, 1)"); } else { - ShowError("The value '" . $isactive . - "' is not a valid value for the active flag.
" . - "There may be a problem with Bugzilla or a bug in your browser.
" . - "Update of active flag for group $v skipped."); + print " cleared "; + SendSQL("DELETE FROM group_group_map + WHERE member_id = $v AND grantor_id = $gid + AND isbless = 1"); } } + } } if (!$chgs) { print "You didn't change anything!
\n"; print "If you really meant it, hit the Back button and try again.

\n"; } else { + SendSQL("UPDATE groups SET last_changed = NOW() WHERE id = $gid"); print "Done.

\n"; } PutTrailer("Back to the group list"); exit; } + + # # No valid action found # diff --git a/editproducts.cgi b/editproducts.cgi index 5b4c3c249..18ad4216d 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -79,9 +79,9 @@ sub CheckProduct ($) # Displays the form to edit a products parameters # -sub EmitFormElements ($$$$$$$$$) +sub EmitFormElements ($$$$$$$$) { - my ($product, $description, $milestoneurl, $userregexp, $disallownew, + my ($product, $description, $milestoneurl, $disallownew, $votesperuser, $maxvotesperbug, $votestoconfirm, $defaultmilestone) = @_; @@ -110,13 +110,6 @@ sub EmitFormElements ($$$$$$$$$) print qq{\n}; } - # Added -JMR, 2/16/00 - if (Param("usebuggroups")) { - $userregexp = value_quote($userregexp); - print "

\n"; - print " \n"; - print " \n"; - } print "\n"; print " \n"; @@ -263,7 +256,7 @@ if ($action eq 'add') { print "\n"; print "
BitIdNameDescription
$bit$gid$name$desc
User Regexp for Bug Group:
Closed for bug entry:
\n"; - EmitFormElements('', '', '', '', 0, 0, 10000, 0, "---"); + EmitFormElements('', '', '', 0, 0, 10000, 0, "---"); print "\n"; print " \n"; @@ -315,7 +308,6 @@ if ($action eq 'new') { my $description = trim($::FORM{description} || ''); my $milestoneurl = trim($::FORM{milestoneurl} || ''); - my $userregexp = trim($::FORM{userregexp} || ''); my $disallownew = 0; $disallownew = 1 if $::FORM{disallownew}; my $votesperuser = $::FORM{votesperuser}; @@ -351,48 +343,20 @@ if ($action eq 'new') { # If we're using bug groups, then we need to create a group for this # product as well. -JMR, 2/16/00 if(Param("usebuggroups")) { - # First we need to figure out the bit for this group. We'll simply - # use the next highest bit available. We'll use a minimum bit of 256, - # to leave room for a few more Bugzilla operation groups at the bottom. - SendSQL("SELECT MAX(bit) FROM groups"); - my $bit = FetchOneColumn(); - if($bit < 256) { - $bit = 256; - } else { - $bit = $bit * 2; - } - # Next we insert into the groups table SendSQL("INSERT INTO groups " . - "(bit, name, description, isbuggroup, userregexp) " . + "(name, description, isbuggroup, last_changed) " . "VALUES (" . - $bit . ", " . SqlQuote($product) . ", " . - SqlQuote($product . " Bugs Access") . ", " . - "1, " . - SqlQuote($userregexp) . ")"); + SqlQuote("Access to bugs in the $product product") . ", 1, NOW())"); + SendSQL("SELECT last_insert_id()"); + my $gid = FetchOneColumn(); + my $admin = GroupNameToId('admin'); + SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless) + VALUES ($admin, $gid, 0)"); + SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless) + VALUES ($admin, $gid, 1)"); - # And last, we need to add any existing users that match the regexp - # to the group. - # There may be a better way to do this in MySql, but I need to compare - # the login_names to this regexp, and the only way I can think of to - # do that is to get the list of login_names, and then update them - # one by one if they match. Furthermore, I need to do it with two - # separate loops, since opening a new SQL statement to do the update - # seems to clobber the previous one. - - # Modified, 7/17/00, Joe Robins - # If the userregexp is left empty, then no users should be added to - # the bug group. As is, it was adding all users, since they all - # matched the empty pattern. - # In addition, I've replaced the rigamarole I was going through to - # find matching users with a much simpler statement that lets the - # mySQL database do the work. - unless($userregexp eq "") { - SendSQL("UPDATE profiles ". - "SET groupset = groupset | " . $bit . " " . - "WHERE LOWER(login_name) REGEXP LOWER(" . SqlQuote($userregexp) . ")"); - } } # Make versioncache flush @@ -444,22 +408,6 @@ if ($action eq 'del') { print " \n"; } - # Added -JMR, 2/16/00 - if(Param('usebuggroups')) { - # Get the regexp for this product. - SendSQL("SELECT userregexp - FROM groups - WHERE name=" . SqlQuote($product)); - my $userregexp = FetchOneColumn(); - if(!defined $userregexp) { - $userregexp = "undefined"; - } elsif ($userregexp eq "") { - $userregexp = "blank"; - } - print "\n"; - print " \n"; - print " \n"; - } print "\n"; print " \n"; @@ -637,30 +585,6 @@ if ($action eq 'delete') { WHERE id=$product_id"); print "Product '$product' deleted.
\n"; - # Added -JMR, 2/16/00 - if (Param("usebuggroups")) { - # We need to get the bit of the group from the table, then update the - # groupsets of members of that group and remove the group. - SendSQL("SELECT bit, description FROM groups " . - "WHERE name = " . SqlQuote($product)); - my ($bit, $group_desc) = FetchSQLData(); - - # Make sure there is a group before we try to do any deleting... - if($bit) { - # I'm kludging a bit so that I don't break superuser access; - # I'm merely checking to make sure that the groupset is not - # the superuser groupset in doing this update... - SendSQL("UPDATE profiles " . - "SET groupset = groupset - $bit " . - "WHERE (groupset & $bit) " . - "AND (groupset != 9223372036854710271)"); - print "Users dropped from group '$group_desc'.
\n"; - - SendSQL("DELETE FROM groups " . - "WHERE bit = $bit"); - print "Group '$group_desc' deleted.
\n"; - } - } SendSQL("UNLOCK TABLES"); @@ -690,18 +614,10 @@ if ($action eq 'edit') { $votesperuser, $maxvotesperbug, $votestoconfirm, $defaultmilestone) = FetchSQLData(); - my $userregexp = ''; - if(Param("usebuggroups")) { - SendSQL("SELECT userregexp - FROM groups - WHERE name=" . SqlQuote($product)); - $userregexp = FetchOneColumn() || ""; - } - print "\n"; print "
Version:$milestonelink
User Regexp for Bug Group:$userregexp
Closed for bugs:
\n"; - EmitFormElements($product, $description, $milestoneurl, $userregexp, + EmitFormElements($product, $description, $milestoneurl, $disallownew, $votesperuser, $maxvotesperbug, $votestoconfirm, $defaultmilestone); @@ -787,10 +703,6 @@ if ($action eq 'edit') { value_quote($description) . "\">\n"; print "\n"; - if(Param("usebuggroups")) { - print "\n"; - } print "\n"; print "\n"; print "\n"; @@ -826,8 +738,6 @@ if ($action eq 'update') { my $milestoneurlold = trim($::FORM{milestoneurlold} || ''); my $votesperuser = trim($::FORM{votesperuser} || 0); my $votesperuserold = trim($::FORM{votesperuserold} || 0); - my $userregexp = trim($::FORM{userregexp} || ''); - my $userregexpold = trim($::FORM{userregexpold} || ''); my $maxvotesperbug = trim($::FORM{maxvotesperbug} || 0); my $maxvotesperbugold = trim($::FORM{maxvotesperbugold} || 0); my $votestoconfirm = trim($::FORM{votestoconfirm} || 0); @@ -883,68 +793,6 @@ if ($action eq 'update') { print "Updated mile stone URL.
\n"; } - # Added -JMR, 2/16/00 - if (Param("usebuggroups") && $userregexp ne $userregexpold) { - # This will take a little bit of work here, since there may not be - # an existing bug group for this product, and we will also have to - # update users groupsets. - # First we find out if there's an existing group for this product, and - # get its bit if there is. - SendSQL("SELECT bit " . - "FROM groups " . - "WHERE name = " . SqlQuote($productold)); - my $bit = FetchOneColumn(); - if($bit) { - # Group exists, so we do an update statement. - SendSQL("UPDATE groups " . - "SET userregexp = " . SqlQuote($userregexp) . " " . - "WHERE name = " . SqlQuote($productold)); - print "Updated user regexp for bug group.
\n"; - } else { - # Group doesn't exist. Let's make it, the same way as we make a - # group for a new product above. - SendSQL("SELECT MAX(bit) FROM groups"); - my $tmp_bit = FetchOneColumn(); - if($tmp_bit < 256) { - $bit = 256; - } else { - $bit = $tmp_bit * 2; - } - SendSQL("INSERT INTO groups " . - "(bit, name, description, isbuggroup, userregexp) " . - "values (" . $bit . ", " . - SqlQuote($productold) . ", " . - SqlQuote($productold . " Bugs Access") . ", " . - "1, " . - SqlQuote($userregexp) . ")"); - print "Created bug group.
\n"; - } - - # And now we have to update the profiles again to add any users who - # match the new regexp to the group. I'll do this the same way as - # when I create a new group above. Note that I'm not taking out - # users who matched the old regexp and not the new one; that would - # be insanely messy. Use the group administration page for that - # instead. - SendSQL("SELECT login_name FROM profiles"); - my @login_list = (); - my $this_login; - while($this_login = FetchOneColumn()) { - push @login_list, $this_login; - } - my $updated_profiles = 0; - foreach $this_login (@login_list) { - if($this_login =~ /$userregexp/) { - SendSQL("UPDATE profiles " . - "SET groupset = groupset | " . $bit . " " . - "WHERE login_name = " . SqlQuote($this_login)); - $updated_profiles = 1; - } - } - if($updated_profiles) { - print "Added users matching regexp to group.
\n"; - } - } if ($votesperuser ne $votesperuserold) { SendSQL("UPDATE products @@ -1012,9 +860,10 @@ if ($action eq 'update') { # update it so it will match in the future. If there is no group, this # update statement will do nothing, so no harm done. -JMR, 3/8/00 SendSQL("UPDATE groups " . - "SET name=$qp, " . - "description=".SqlQuote($product." Bugs Access")." ". - "WHERE name=$qpold"); + "SET name = $qp, " . + "description = " . + SqlQuote("Access to bugs in the $product product") . + " WHERE name = $qpold"); print "Updated product name.
\n"; } diff --git a/editusers.cgi b/editusers.cgi index 4586345af..cc6be6665 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -22,6 +22,7 @@ # Dave Miller # Joe Robins # Dan Mosedale +# Joel Peshkin # # Direct any questions on this source code to # @@ -39,11 +40,9 @@ require "globals.pl"; sub sillyness { my $zz; $zz = $::userid; - $zz = $::superusergroupset; } my $editall; -my $opblessgroupset = '9223372036854775807'; # This is all 64 bits. @@ -97,9 +96,9 @@ sub EmitElement ($$) # Displays the form to edit a user parameters # -sub EmitFormElements ($$$$$) +sub EmitFormElements ($$$$) { - my ($user, $realname, $groupset, $blessgroupset, $disabledtext) = @_; + my ($user_id, $user, $realname, $disabledtext) = @_; print " \n"; EmitElement("user", $user); @@ -134,11 +133,15 @@ sub EmitFormElements ($$$$$) if($user ne "") { print "\n"; - - print "\n"; } @@ -238,9 +241,7 @@ print "Content-type: text/html\n\n"; $editall = UserInGroup("editusers"); if (!$editall) { - SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $::userid"); - $opblessgroupset = FetchOneColumn(); - if (!$opblessgroupset) { + if (!UserCanBlessAnything()) { PutHeader("Not allowed"); print "Sorry, you aren't a member of the 'editusers' group, and you\n"; print "don't have permissions to put people in or out of any group.\n"; @@ -316,6 +317,10 @@ if ($action eq 'list') { } elsif (exists $::FORM{'query'}) { $query = "SELECT login_name,realname,disabledtext " . "FROM profiles WHERE " . $::FORM{'query'} . " ORDER BY login_name"; + } elsif (exists $::FORM{'group'}) { + $query = "SELECT DISTINCT login_name,realname,disabledtext " . + "FROM profiles, user_group_map WHERE profiles.userid = user_group_map.user_id + AND group_id=" . $::FORM{'group'} . " ORDER BY login_name"; } else { die "Missing parameters"; } @@ -396,7 +401,7 @@ if ($action eq 'add') { print "\n"; print "
Login name:
Group Access:"; - SendSQL("SELECT bit,name,description,bit & $groupset != 0, " . - " bit & $blessgroupset " . + SendSQL("SELECT groups.id, groups.name, groups.description, " . + "COUNT(user_id), " . + "MAX(isderived) " . "FROM groups " . - "WHERE bit & $opblessgroupset != 0 AND isbuggroup " . - "ORDER BY name"); + "LEFT JOIN user_group_map " . + "ON user_group_map.group_id = groups.id " . + "AND isbless = 0 " . + "AND user_id = $user_id " . + "GROUP BY groups.name "); if (MoreSQLData()) { if ($editall) { print "\n"; @@ -146,50 +149,50 @@ sub EmitFormElements ($$$$$) } print "\n"; while (MoreSQLData()) { - my ($bit,$name,$description,$checked,$blchecked) = FetchSQLData(); - print "\n"; + my ($groupid, $name, $description, $member, $isderived) = FetchSQLData(); + next if (!$editall && !UserCanBlessGroup($name)); + $isderived = $isderived || 0; + my $checked = $member - $isderived; + PushGlobalSQLState(); + SendSQL("SELECT user_id " . + "FROM user_group_map " . + "WHERE isbless = 1 " . + "AND user_id = $user_id " . + "AND group_id = $groupid"); + my ($blchecked) = FetchSQLData() ? 1 : 0; + SendSQL("SELECT grantor_id FROM user_group_map, + group_group_map + WHERE $groupid = grantor_id + AND user_group_map.user_id = $user_id + AND user_group_map.isbless = 0 + AND group_group_map.isbless = 1 + AND user_group_map.group_id = member_id"); + my $derivedbless = FetchOneColumn(); + PopGlobalSQLState(); + print "\n"; + print "\n"; + print "\n"; if ($editall) { $blchecked = ($blchecked) ? "CHECKED" : ""; - print ""; + print "\n"; } $checked = ($checked) ? "CHECKED" : ""; - print ""; - print "\n"; - } - } - print "
Can turn this bit on for other usersUser is a member of these groups
"; + print "[" if $derivedbless; + print ""; + print "]" if $derivedbless; + print "" . ucfirst($name) . ": $description
Privileges:"; - SendSQL("SELECT bit,name,description,bit & $groupset != 0, " . - " bit & $blessgroupset " . - "FROM groups " . - "WHERE bit & $opblessgroupset != 0 AND !isbuggroup " . - "ORDER BY name"); - if (MoreSQLData()) { - if ($editall) { - print "\n"; - print "\n\n"; - } - print "\n"; - while (MoreSQLData()) { - my ($bit,$name,$description,$checked,$blchecked) = FetchSQLData(); - print "\n"; - if ($editall) { - $blchecked = ($blchecked) ? "CHECKED" : ""; - print ""; + print "\n"; } - $checked = ($checked) ? "CHECKED" : ""; - print ""; - print "\n"; } } - } else { - print "\n"; print "
Can turn this bit on for other users
|User has these privileges
"; + print '[' if ($isderived); + print ""; + print ']' if ($isderived); + print ""; + print ucfirst($name) . ": $description" . ucfirst($name) . ": $description
Groups and
Privileges:
"; - print "\n"; - } + print "
The new user will be inserted into groups " . - "based on their userregexps.
To change the group " . - "permissions for this user, you must edit the account after ". - "creating it.
\n"; - EmitFormElements('', '', 0, 0, ''); + EmitFormElements(0, '', '', ''); print "
\n
\n"; print "\n"; @@ -465,48 +470,22 @@ if ($action eq 'new') { exit; } - # For new users, we use the regexps from the groups table to determine - # their initial group membership. - # We also keep a list of groups the user was added to for display on the - # confirmation page. - my $bits = "0"; - my @grouplist = (); - SendSQL("select bit, name, userregexp from groups where userregexp != ''"); - while (MoreSQLData()) { - my @row = FetchSQLData(); - if ($user =~ m/$row[2]/i) { - $bits .= "+ $row[0]"; # Silly hack to let MySQL do the math, - # not Perl, since we're dealing with 64 - # bit ints here, and I don't *think* Perl - # does that. - push(@grouplist, $row[1]); - } - } - # Add the new user SendSQL("INSERT INTO profiles ( " . - "login_name, cryptpassword, realname, groupset, " . + "login_name, cryptpassword, realname, " . "disabledtext" . " ) VALUES ( " . SqlQuote($user) . "," . SqlQuote(Crypt($password)) . "," . SqlQuote($realname) . "," . - $bits . "," . SqlQuote($disabledtext) . ")" ); #+++ send e-mail away print "OK, done.
\n"; - if($#grouplist > -1) { - print "New user added to these groups based on group regexps:\n"; - print "
    \n"; - foreach (@grouplist) { - print "
  • $_
  • \n"; - } - print "
\n"; - } else { - print "New user not added to any groups.

\n"; - } + SendSQL("SELECT last_insert_id()"); + my ($newuserid) = FetchSQLData(); + DeriveGroup($newuserid); print "To change ${user}'s permissions, go back and edit this user"; print "

\n"; PutTrailer($localtrailer, @@ -538,9 +517,9 @@ if ($action eq 'del') { CheckUser($user); # display some data about the user - SendSQL("SELECT realname, groupset FROM profiles + SendSQL("SELECT userid, realname FROM profiles WHERE login_name=" . SqlQuote($user)); - my ($realname, $groupset) = + my ($thisuserid, $realname) = FetchSQLData(); $realname = ($realname ? html_quote($realname) : "missing"); @@ -561,9 +540,11 @@ if ($action eq 'del') { print "

Group set:"; SendSQL("SELECT name - FROM groups - WHERE bit & $groupset = bit - ORDER BY isbuggroup, name"); + FROM groups, user_group_map + WHERE groups.id = user_group_map.group_id + AND user_group_map.user_id = $thisuserid + AND isbless = 0 + ORDER BY name"); my $found = 0; while ( MoreSQLData() ) { my ($name) = FetchSQLData(); @@ -697,27 +678,34 @@ if ($action eq 'edit') { CheckUser($user); # get data of user - SendSQL("SELECT realname, groupset, blessgroupset, disabledtext + SendSQL("SELECT userid, realname, disabledtext FROM profiles WHERE login_name=" . SqlQuote($user)); - my ($realname, $groupset, $blessgroupset, - $disabledtext) = FetchSQLData(); + my ($thisuserid, $realname, $disabledtext) = FetchSQLData(); + if ($thisuserid > 0) { + DeriveGroup($thisuserid); + } print "\n"; print "\n"; - EmitFormElements($user, $realname, $groupset, $blessgroupset, $disabledtext); + EmitFormElements($thisuserid, $user, $realname, $disabledtext); print "
\n"; - print "\n"; print "\n"; - print "\n"; - print "\n"; print "\n"; print "\n"; print "\n"; + print "
User is a member of any groups shown with grey bars and + marked with brackets surrounding the membership checkbox as a + result of a regular expression match + or membership in another group. + User can bless any group + marked with brackets surrounding the bless checkbox as a + result of membership in another group. +
"; print ""; @@ -740,69 +728,67 @@ if ($action eq 'update') { my $password = $::FORM{password} || ''; my $disabledtext = trim($::FORM{disabledtext} || ''); my $disabledtextold = trim($::FORM{disabledtextold} || ''); - my $groupsetold = trim($::FORM{groupsetold} || '0'); - my $blessgroupsetold = trim($::FORM{blessgroupsetold} || '0'); - my $groupset = "0"; - foreach (keys %::FORM) { - next unless /^bit_/; - #print "$_=$::FORM{$_}
\n"; - detaint_natural($::FORM{$_}) || die "Groupset field tampered with"; - $groupset .= " + $::FORM{$_}"; + CheckUser($userold); + SendSQL("SELECT userid FROM profiles + WHERE login_name=" . SqlQuote($userold)); + my ($thisuserid) = FetchSQLData(); + + my @grpadd = (); + my @grpdel = (); + my $chggrp = 0; + SendSQL("SELECT id, name FROM groups"); + while (my ($groupid, $name) = FetchSQLData()) { + if ($::FORM{"oldgroup_$groupid"} != ($::FORM{"group_$groupid"} ? 1 : 0)) { + # group membership changed + PushGlobalSQLState(); + $chggrp = 1; + SendSQL("DELETE FROM user_group_map + WHERE user_id = $thisuserid + AND group_id = $groupid + AND isbless = 0 + AND isderived = 0"); + if ($::FORM{"group_$groupid"}) { + SendSQL("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($thisuserid, $groupid, 0, 0)"); + print "Added user to group $name
\n"; + push(@grpadd, $name); + } else { + print "Dropped user from group $name
\n"; + push(@grpdel, $name); + } + PopGlobalSQLState(); + } + if ($editall && ($::FORM{"oldbless_$groupid"} != ($::FORM{"bless_$groupid"} ? 1 : 0))) { + # group membership changed + PushGlobalSQLState(); + SendSQL("DELETE FROM user_group_map + WHERE user_id = $thisuserid + AND group_id = $groupid + AND isbless = 1 + AND isderived = 0"); + if ($::FORM{"bless_$groupid"}) { + SendSQL("INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES ($thisuserid, $groupid, 1, 0)"); + print "Granted user permission to bless group $name
\n"; + } else { + print "Revoked user's permission to bless group $name
\n"; + } + PopGlobalSQLState(); + + } } - my $blessgroupset = "0"; - foreach (keys %::FORM) { - next unless /^blbit_/; - #print "$_=$::FORM{$_}
\n"; - detaint_natural($::FORM{$_}) || die "Blessgroupset field tampered with"; - $blessgroupset .= " + $::FORM{$_}"; + my $fieldid = GetFieldID("bug_group"); + if ($chggrp) { + SendSQL("INSERT INTO profiles_activity " . + "(userid, who, profiles_when, fieldid, oldvalue, newvalue) " . + "VALUES " . "($thisuserid, $::userid, now(), $fieldid, " . + SqlQuote(join(", ",@grpdel)) . ", " . + SqlQuote(join(", ",@grpadd)) . ")"); } - CheckUser($userold); - - # Note that the order of this tests is important. If you change - # them, be sure to test for WHERE='$product' or WHERE='$productold' - - if ($groupset ne $groupsetold) { - SendSQL("SELECT groupset FROM profiles WHERE login_name=" . - SqlQuote($userold)); - $groupsetold = FetchOneColumn(); - # Updated, 5/7/00, Joe Robins - # We don't want to change the groupset of a superuser. - if($groupsetold eq $::superusergroupset) { - print "Cannot change permissions of superuser.\n"; - } else { - SendSQL("UPDATE profiles - SET groupset = - groupset - (groupset & $opblessgroupset) + - (($groupset) & $opblessgroupset) - WHERE login_name=" . SqlQuote($userold)); - - # I'm paranoid that someone who I give the ability to bless people - # will start misusing it. Let's log who blesses who (even though - # nothing actually uses this log right now). - my $fieldid = GetFieldID("groupset"); - SendSQL("SELECT userid, groupset FROM profiles WHERE login_name=" . - SqlQuote($userold)); - my $u; - ($u, $groupset) = (FetchSQLData()); - if ($groupset ne $groupsetold) { - SendSQL("INSERT INTO profiles_activity " . - "(userid,who,profiles_when,fieldid,oldvalue,newvalue) " . - "VALUES " . - "($u, $::userid, now(), $fieldid, " . - " $groupsetold, $groupset)"); - } - print "Updated permissions.\n"; - } - } - - if ($editall && $blessgroupset ne $blessgroupsetold) { - SendSQL("UPDATE profiles - SET blessgroupset=" . $blessgroupset . " - WHERE login_name=" . SqlQuote($userold)); - print "Updated ability to tweak permissions of other users.\n"; - } # Update the database with the user's new password if they changed it. if ( !Param('useLDAP') && $editall && $password ) { diff --git a/enter_bug.cgi b/enter_bug.cgi index a779aa85d..a7733ec3e 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -48,6 +48,7 @@ use vars qw( @legal_platform @legal_priority @legal_severity + $userid %MFORM %versions ); @@ -326,60 +327,58 @@ $default{'bug_status'} = $status[0]; # Select whether to restrict this bug to the product's bug group or not, # if the usebuggroups parameter is set, and if this product has a bug group. -if ($::usergroupset ne '0') { - # First we get the bit and description for the group. - my $group_bit = '0'; - - if(Param("usebuggroups") && GroupExists($product)) { - SendSQL("SELECT bit FROM groups ". - "WHERE name = " . SqlQuote($product) . " " . - "AND isbuggroup != 0"); - ($group_bit) = FetchSQLData(); - } - - SendSQL("SELECT bit, name, description FROM groups " . - "WHERE bit & $::usergroupset != 0 " . - "AND isbuggroup != 0 AND isactive = 1 ORDER BY description"); - - my @groups; - - while (MoreSQLData()) { - my ($bit, $prodname, $description) = FetchSQLData(); - # Don't want to include product groups other than this product. - next unless($prodname eq $product || - !defined($::proddesc{$prodname})); +# First we get the bit and description for the group. +my $group_id = '0'; - my $check; +if(Param("usebuggroups")) { + ($group_id) = GroupExists($product); +} - # If this is the group for this product, make it checked. - if(formvalue("maketemplate") eq - "Remember values as bookmarkable template") - { - # If this is a bookmarked template, then we only want to set the - # bit for those bits set in the template. - $check = formvalue("bit-$bit", 0); - } - else { - # $group_bit will only have a non-zero value if we're using - # bug groups and have one for this product. - # If $group_bit is 0, it won't match the current group, so compare - # it to the current bit instead of checking for non-zero. - $check = ($group_bit == $bit); - } +SendSQL("SELECT DISTINCT groups.id, groups.name, groups.description " . + "FROM groups, user_group_map " . + "WHERE user_group_map.group_id = groups.id " . + "AND user_group_map.user_id = $::userid " . + "AND isbless = 0 " . + "AND isbuggroup = 1 AND isactive = 1 ORDER BY description"); - my $group = - { - 'bit' => $bit , - 'checked' => $check , - 'description' => $description - }; +my @groups; - push @groups, $group; +while (MoreSQLData()) { + my ($id, $prodname, $description) = FetchSQLData(); + # Don't want to include product groups other than this product. + next unless(!Param("usebuggroups") || $prodname eq $product || + !defined($::proddesc{$prodname})); + + my $check; + + # If this is the group for this product, make it checked. + if(formvalue("maketemplate") eq + "Remember values as bookmarkable template") + { + # If this is a bookmarked template, then we only want to set the + # bit for those bits set in the template. + $check = formvalue("bit-$id", 0); + } + else { + # $group_bit will only have a non-zero value if we're using + # bug groups and have one for this product. + # If $group_bit is 0, it won't match the current group, so compare + # it to the current bit instead of checking for non-zero. + $check = ($group_id == $id); } - $vars->{'group'} = \@groups; + my $group = + { + 'bit' => $id , + 'checked' => $check , + 'description' => $description + }; + + push @groups, $group; } +$vars->{'group'} = \@groups; + $vars->{'default'} = \%default; my $format = @@ -388,3 +387,4 @@ my $format = print "Content-type: $format->{'ctype'}\n\n"; $template->process($format->{'template'}, $vars) || ThrowTemplateError($template->error()); + diff --git a/globals.pl b/globals.pl index 4570a5658..242c8a5f6 100644 --- a/globals.pl +++ b/globals.pl @@ -22,6 +22,7 @@ # Jacob Steenhagen # Bradley Baetz # Christopher Aillon +# Joel Peshkin # Contains some global variables and routines used throughout bugzilla. @@ -57,7 +58,6 @@ sub globals_pl_sillyness { $zz = @main::milestoneurl; $zz = %main::proddesc; $zz = @main::prodmaxvotes; - $zz = $main::superusergroupset; $zz = $main::template; $zz = $main::userid; $zz = $main::vars; @@ -102,10 +102,6 @@ $::defaultqueryname = "(Default query)"; $::unconfirmedstate = "UNCONFIRMED"; $::dbwritesallowed = 1; -# Adding a global variable for the value of the superuser groupset. -# Joe Robins, 7/5/00 -$::superusergroupset = "9223372036854775807"; - #sub die_with_dignity { # my ($err_msg) = @_; # print $err_msg; @@ -583,30 +579,14 @@ sub InsertNewUser { my $password = GenerateRandomPassword(); my $cryptpassword = Crypt($password); - # Determine what groups the user should be in by default - # and add them to those groups. - PushGlobalSQLState(); - SendSQL("select bit, userregexp from groups where userregexp != ''"); - my $groupset = "0"; - while (MoreSQLData()) { - my @row = FetchSQLData(); - # Modified -Joe Robins, 2/17/00 - # Making this case insensitive, since usernames are email addresses, - # and could be any case. - if ($username =~ m/$row[1]/i) { - $groupset .= "+ $row[0]"; # Silly hack to let MySQL do the math, - # not Perl, since we're dealing with 64 - # bit ints here, and I don't *think* Perl - # does that. - } - } # Insert the new user record into the database. $username = SqlQuote($username); $realname = SqlQuote($realname); $cryptpassword = SqlQuote($cryptpassword); - SendSQL("INSERT INTO profiles (login_name, realname, cryptpassword, groupset) - VALUES ($username, $realname, $cryptpassword, $groupset)"); + PushGlobalSQLState(); + SendSQL("INSERT INTO profiles (login_name, realname, cryptpassword) + VALUES ($username, $realname, $cryptpassword)"); PopGlobalSQLState(); # Return the password to the calling code so it can be included @@ -650,98 +630,47 @@ sub GenerateRandomPassword { return $password; } -sub SelectVisible { - my ($query, $userid, $usergroupset) = @_; - - # Run the SQL $query with the additional restriction that - # the bugs can be seen by $userid. $usergroupset is provided - # as an optimisation when this is already known, eg from CGI.pl - # If not present, it will be obtained from the db. - # Assumes that 'bugs' is mentioned as a table name. You should - # also make sure that bug_id is qualified bugs.bug_id! - # Your query must have a WHERE clause. This is unlikely to be a problem. - - # Also, note that mySQL requires aliases for tables to be locked, as well - # This means that if you change the name from selectVisible_cc (or add - # additional tables), you will need to update anywhere which does a - # LOCK TABLE, and then calls routines which call this - - $usergroupset = 0 unless $userid; - - unless (defined($usergroupset)) { - PushGlobalSQLState(); - SendSQL("SELECT groupset FROM profiles WHERE userid = $userid"); - $usergroupset = FetchOneColumn(); - PopGlobalSQLState(); - } - - # Users are authorized to access bugs if they are a member of all - # groups to which the bug is restricted. User group membership and - # bug restrictions are stored as bits within bitsets, so authorization - # can be determined by comparing the intersection of the user's - # bitset with the bug's bitset. If the result matches the bug's bitset - # the user is a member of all groups to which the bug is restricted - # and is authorized to access the bug. - - # A user is also authorized to access a bug if she is the reporter, - # or member of the cc: list of the bug and the bug allows users in those - # roles to see the bug. The boolean fields reporter_accessible and - # cclist_accessible identify whether or not those roles can see the bug. - - # Bit arithmetic is performed by MySQL instead of Perl because bitset - # fields in the database are 64 bits wide (BIGINT), and Perl installations - # may or may not support integers larger than 32 bits. Using bitsets - # and doing bitset arithmetic is probably not cross-database compatible, - # however, so these mechanisms are likely to change in the future. - - my $replace = " "; - - if ($userid) { - $replace .= "LEFT JOIN cc selectVisible_cc ON - bugs.bug_id = selectVisible_cc.bug_id AND - selectVisible_cc.who = $userid " - } - - $replace .= "WHERE ((bugs.groupset & $usergroupset) = bugs.groupset "; - - if ($userid) { - # There is a mysql bug affecting v3.22 and 3.23 (at least), where this will - # cause all rows to be returned! We work arround this by adding an not isnull - # test to the JOINed cc table. See http://lists.mysql.com/cgi-ez/ezmlm-cgi?9:mss:11417 - # Its needed, even though it shouldn't be - $replace .= "OR (bugs.reporter_accessible = 1 AND bugs.reporter = $userid)" . - " OR (bugs.cclist_accessible = 1 AND selectVisible_cc.who = $userid AND not isnull(selectVisible_cc.who))" . - " OR (bugs.assigned_to = $userid)"; - if (Param("useqacontact")) { - $replace .= " OR (bugs.qa_contact = $userid)"; - } - } - - $replace .= ") AND "; - - $query =~ s/\sWHERE\s/$replace/i; - - return $query; -} - sub CanSeeBug { - # Note that we pass in the usergroupset, since this is known - # in most cases (ie viewing bugs). Maybe make this an optional - # parameter? - my ($id, $userid, $usergroupset) = @_; + my ($id, $userid) = @_; # Query the database for the bug, retrieving a boolean value that # represents whether or not the user is authorized to access the bug. + # if no groups are found --> user is permitted to access + # if no user is found for any group --> user is not permitted to access + my $query = "SELECT bugs.bug_id, reporter, assigned_to, qa_contact," . + " reporter_accessible, cclist_accessible," . + " cc.who IS NOT NULL," . + " COUNT(bug_group_map.group_id) as cntbugingroups," . + " COUNT(DISTINCT(user_group_map.group_id)) as cntuseringroups" . + " FROM bugs" . + " LEFT JOIN cc ON bugs.bug_id = cc.bug_id" . + " AND cc.who = $userid" . + " LEFT JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id" . + " LEFT JOIN user_group_map ON" . + " user_group_map.group_id = bug_group_map.group_id" . + " AND user_group_map.isbless = 0" . + " AND user_group_map.user_id = $userid" . + " WHERE bugs.bug_id = $id GROUP BY bugs.bug_id"; PushGlobalSQLState(); - SendSQL(SelectVisible("SELECT bugs.bug_id FROM bugs WHERE bugs.bug_id = $id", - $userid, $usergroupset)); - - my $ret = defined(FetchSQLData()); + SendSQL($query); + my ($found_id, $reporter, $assigned_to, $qa_contact, + $rep_access, $cc_access, + $found_cc, $found_groups, $found_members) + = FetchSQLData(); PopGlobalSQLState(); - - return $ret; + return ( + ($found_groups == 0) + || (($userid > 0) && + ( + ($assigned_to == $userid) + || ($qa_contact == $userid) + || (($reporter == $userid) && $rep_access) + || ($found_cc && $cc_access) + || ($found_groups == $found_members) + )) + ); } sub ValidatePassword { @@ -799,6 +728,94 @@ sub Crypt { return $cryptedpassword; } +# ConfirmGroup(userid) is called prior to any activity that relies +# on user_group_map to ensure that derived group permissions are up-to-date. +# Permissions must be rederived if ANY groups have a last_changed newer +# than the profiles.refreshed_when value. +sub ConfirmGroup { + my ($user) = (@_); + PushGlobalSQLState(); + SendSQL("SELECT userid FROM profiles, groups WHERE userid = $user " . + "AND profiles.refreshed_when <= groups.last_changed "); + my $ret = FetchSQLData(); + PopGlobalSQLState(); + if ($ret) { + DeriveGroup($user); + } +} + +# DeriveGroup removes and rederives all derived group permissions for +# the specified user. +sub DeriveGroup { + my ($user) = (@_); + PushGlobalSQLState(); + + SendSQL("LOCK TABLES profiles WRITE, user_group_map WRITE, group_group_map READ, groups READ"); + + # avoid races, we are only as up to date as the BEGINNING of this process + SendSQL("SELECT login_name, NOW() FROM profiles WHERE userid = $user"); + my ($login, $starttime) = FetchSQLData(); + + # first remove any old derived stuff for this user + SendSQL("DELETE FROM user_group_map WHERE user_id = $user " . + "AND isderived = 1"); + + my %groupidsadded = (); + # add derived records for any matching regexps + SendSQL("SELECT id, userregexp FROM groups WHERE userregexp != ''"); + while (MoreSQLData()) { + my ($groupid, $rexp) = FetchSQLData(); + if ($login =~ m/$rexp/i) { + PushGlobalSQLState(); + $groupidsadded{$groupid} = 1; + SendSQL("INSERT INTO user_group_map " . + "(user_id, group_id, isbless, isderived) " . + "VALUES ($user, $groupid, 0, 1)"); + PopGlobalSQLState(); + + } + } + + # Get a list of the groups of which the user is a member. + my %groupidschecked = (); + my @groupidstocheck = (); + SendSQL("SELECT group_id FROM user_group_map WHERE user_id = $user + AND NOT isbless"); + while (MoreSQLData()) { + my ($groupid) = FetchSQLData(); + push(@groupidstocheck,$groupid); + } + + # Each group needs to be checked for inherited memberships once. + while (@groupidstocheck) { + my $group = shift @groupidstocheck; + if (!defined($groupidschecked{"$group"})) { + $groupidschecked{"$group"} = 1; + SendSQL("SELECT grantor_id FROM group_group_map WHERE" + . " member_id = $group AND NOT isbless"); + while (MoreSQLData()) { + my ($groupid) = FetchSQLData(); + if (!defined($groupidschecked{"$groupid"})) { + push(@groupidstocheck,$groupid); + } + if (!$groupidsadded{$groupid}) { + $groupidsadded{$groupid} = 1; + PushGlobalSQLState(); + SendSQL("INSERT INTO user_group_map" + . " (user_id, group_id, isbless, isderived)" + . " VALUES ($user, $groupid, 0, 1)"); + PopGlobalSQLState(); + } + } + } + } + + SendSQL("UPDATE profiles SET refreshed_when = " . + SqlQuote($starttime) . "WHERE userid = $user"); + SendSQL("UNLOCK TABLES"); + PopGlobalSQLState(); +}; + sub DBID_to_real_or_loginname { my ($id) = (@_); @@ -860,6 +877,9 @@ sub DBNameToIdAndCheck { ThrowUserError("invalid_username"); } + + + sub get_product_id { my ($prod) = @_; PushGlobalSQLState(); @@ -1015,12 +1035,12 @@ sub GetBugLink { # is saved off rather than overwritten PushGlobalSQLState(); - SendSQL("SELECT bugs.bug_status, resolution, short_desc, groupset " . + SendSQL("SELECT bugs.bug_status, resolution, short_desc " . "FROM bugs WHERE bugs.bug_id = $bug_num"); # If the bug exists, save its data off for use later in the sub if (MoreSQLData()) { - my ($bug_state, $bug_res, $bug_desc, $bug_grp) = FetchSQLData(); + my ($bug_state, $bug_res, $bug_desc) = FetchSQLData(); # Initialize these variables to be "" so that we don't get warnings # if we don't change them below (which is highly likely). my ($pre, $title, $post) = ("", "", ""); @@ -1035,7 +1055,7 @@ sub GetBugLink { $title .= " $bug_res"; $post = ""; } - if ($bug_grp == 0 || CanSeeBug($bug_num, $::userid, $::usergroupset)) { + if (CanSeeBug($bug_num, $::userid)) { $title .= " - $bug_desc"; } $::buglink{$bug_num} = [$pre, value_quote($title), $post]; @@ -1186,15 +1206,100 @@ sub SqlQuote { return "'$str'"; } + +# UserInGroup returns information aboout the current user if no second +# parameter is specified sub UserInGroup { - return $::vars->{'user'}{'groups'}{$_[0]}; + my ($groupname, $userid) = (@_); + if (!$userid) { + return $::vars->{'user'}{'groups'}{$_[0]}; + } + PushGlobalSQLState(); + $userid ||= $::userid; + SendSQL("SELECT groups.id FROM groups, user_group_map + WHERE groups.id = user_group_map.group_id + AND user_group_map.user_id = $userid + AND isbless = 0 + AND groups.name = " . SqlQuote($groupname)); + my $result = FetchOneColumn(); + PopGlobalSQLState(); + return defined($result); +} + +sub UserCanBlessGroup { + my ($groupname) = (@_); + PushGlobalSQLState(); + # check if user explicitly can bless group + SendSQL("SELECT groups.id FROM groups, user_group_map + WHERE groups.id = user_group_map.group_id + AND user_group_map.user_id = $::userid + AND isbless = 1 + AND groups.name = " . SqlQuote($groupname)); + my $result = FetchOneColumn(); + PopGlobalSQLState(); + if ($result) { + return 1; + } + PushGlobalSQLState(); + # check if user is a member of a group that can bless this group + # this group does not count + SendSQL("SELECT groups.id FROM groups, user_group_map, + group_group_map + WHERE groups.id = grantor_id + AND user_group_map.user_id = $::userid + AND user_group_map.isbless = 0 + AND group_group_map.isbless = 1 + AND user_group_map.group_id = member_id + AND groups.name = " . SqlQuote($groupname)); + $result = FetchOneColumn(); + PopGlobalSQLState(); + return $result; +} + +sub UserCanBlessAnything { + PushGlobalSQLState(); + # check if user explicitly can bless a group + SendSQL("SELECT group_id FROM user_group_map + WHERE user_id = $::userid AND isbless = 1"); + my $result = FetchOneColumn(); + PopGlobalSQLState(); + if ($result) { + return 1; + } + PushGlobalSQLState(); + # check if user is a member of a group that can bless this group + SendSQL("SELECT groups.id FROM groups, user_group_map, + group_group_map + WHERE groups.id = grantor_id + AND user_group_map.user_id = $::userid + AND group_group_map.isbless = 1 + AND user_group_map.group_id = member_id"); + $result = FetchOneColumn(); + PopGlobalSQLState(); + if ($result) { + return 1; + } + return 0; } sub BugInGroup { my ($bugid, $groupname) = (@_); - my $groupbit = GroupNameToBit($groupname); PushGlobalSQLState(); - SendSQL("SELECT (bugs.groupset & $groupbit) != 0 FROM bugs WHERE bugs.bug_id = $bugid"); + SendSQL("SELECT bug_group_map.bug_id != 0 FROM bug_group_map, groups + WHERE bug_group_map.bug_id = $bugid + AND bug_group_map.group_id = groups.id + AND groups.name = " . SqlQuote($groupname)); + my $bugingroup = FetchOneColumn(); + PopGlobalSQLState(); + return $bugingroup; +} + +sub BugInGroupId { + my ($bugid, $groupid) = (@_); + PushGlobalSQLState(); + SendSQL("SELECT bug_id != 0 FROM bug_group_map + WHERE bug_id = $bugid + AND group_id = $groupid"); my $bugingroup = FetchOneColumn(); PopGlobalSQLState(); return $bugingroup; @@ -1203,32 +1308,39 @@ sub BugInGroup { sub GroupExists { my ($groupname) = (@_); PushGlobalSQLState(); - SendSQL("select count(*) from groups where name=" . SqlQuote($groupname)); - my $count = FetchOneColumn(); + SendSQL("SELECT id FROM groups WHERE name=" . SqlQuote($groupname)); + my $id = FetchOneColumn(); PopGlobalSQLState(); - return $count; + return $id; } -# Given the name of an existing group, returns the bit associated with it. -# If the group does not exist, returns 0. -# !!! Remove this function when the new group system is implemented! -sub GroupNameToBit { +sub GroupNameToId { my ($groupname) = (@_); PushGlobalSQLState(); - SendSQL("SELECT bit FROM groups WHERE name = " . SqlQuote($groupname)); - my $bit = FetchOneColumn() || 0; + SendSQL("SELECT id FROM groups WHERE name=" . SqlQuote($groupname)); + my $id = FetchOneColumn(); PopGlobalSQLState(); - return $bit; + return $id; } +sub GroupIdToName { + my ($groupid) = (@_); + PushGlobalSQLState(); + SendSQL("SELECT name FROM groups WHERE id = $groupid"); + my $name = FetchOneColumn(); + PopGlobalSQLState(); + return $name; +} + + # Determines whether or not a group is active by checking # the "isactive" column for the group in the "groups" table. -# Note: This function selects groups by bit rather than by name. +# Note: This function selects groups by id rather than by name. sub GroupIsActive { - my ($groupbit) = (@_); - $groupbit ||= 0; + my ($groupid) = (@_); + $groupid ||= 0; PushGlobalSQLState(); - SendSQL("select isactive from groups where bit=$groupbit"); + SendSQL("SELECT isactive FROM groups WHERE id=$groupid"); my $isactive = FetchOneColumn(); PopGlobalSQLState(); return $isactive; @@ -1561,4 +1673,5 @@ $::vars = 'VERSION' => $Bugzilla::Config::VERSION, }; + 1; diff --git a/index.cgi b/index.cgi index 42f493fcb..7e9fb5aab 100755 --- a/index.cgi +++ b/index.cgi @@ -45,7 +45,6 @@ use vars qw( ConnectToDatabase(); # Check whether or not the user is logged in and, if so, set the $::userid -# and $::usergroupset variables. quietly_check_login(); ############################################################################### diff --git a/long_list.cgi b/long_list.cgi index 6acee0332..6df8a8bad 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -26,7 +26,7 @@ use lib qw(.); require "CGI.pl"; -use vars qw($userid $usergroupset @legal_keywords %FORM); +use vars qw($userid @legal_keywords %FORM); # Use global template variables. use vars qw($template $vars); @@ -69,8 +69,8 @@ my @bugs; foreach my $bug_id (split(/[:,]/, $buglist)) { detaint_natural($bug_id) || next; - SendSQL(SelectVisible("$generic_query AND bugs.bug_id = $bug_id", - $::userid, $::usergroupset)); + CanSeeBug($bug_id, $::userid) || next; + SendSQL("$generic_query AND bugs.bug_id = $bug_id"); my %bug; my @row = FetchSQLData(); diff --git a/post_bug.cgi b/post_bug.cgi index 430ae37a8..22aa0e6bd 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -34,7 +34,6 @@ require "bug_form.pl"; sub sillyness { my $zz; $zz = $::buffer; - $zz = $::usergroupset; $zz = %::COOKIE; $zz = %::components; $zz = %::versions; @@ -242,7 +241,7 @@ if ($::FORM{'keywords'} && UserInGroup("editbugs")) { # Build up SQL string to add bug. my $sql = "INSERT INTO bugs " . - "(" . join(",", @used_fields) . ", reporter, creation_ts, groupset) " . + "(" . join(",", @used_fields) . ", reporter, creation_ts) " . "VALUES ("; foreach my $field (@used_fields) { @@ -255,14 +254,15 @@ $comment = trim($comment); # OK except for the fact that it causes e-mail to be suppressed. $comment = $comment ? $comment : " "; -$sql .= "$::userid, now(), (0"; +$sql .= "$::userid, now() )"; # Groups +my @groupstoadd = (); foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) { if ($::FORM{$b}) { my $v = substr($b, 4); $v =~ /^(\d+)$/ - || ThrowCodeError("group_bit_invalid", "abort"); + || ThrowCodeError("group_id_invalid", "abort"); if (!GroupIsActive($v)) { # Prevent the user from adding the bug to an inactive group. # Should only happen if there is a bug in Bugzilla or the user @@ -271,18 +271,22 @@ foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) { $vars->{'bit'} = $v; ThrowCodeError("inactive_group", "abort"); } - $sql .= " + $v"; # Carefully written so that the math is - # done by MySQL, which can handle 64-bit math, - # and not by Perl, which I *think* can not. + SendSQL("SELECT user_id FROM user_group_map + WHERE user_id = $::userid + AND group_id = $v + AND isbless = 0"); + my ($member) = FetchSQLData(); + if ($member) { + push(@groupstoadd, $v) + } } } -$sql .= ") & $::usergroupset)\n"; # Lock tables before inserting records for the new bug into the database # if we are using a shadow database to prevent shadow database corruption # when two bugs get created at the same time. -SendSQL("LOCK TABLES bugs WRITE, longdescs WRITE, cc WRITE, profiles READ") if Param("shadowdb"); +SendSQL("LOCK TABLES bugs WRITE, bug_group_map WRITE, longdescs WRITE, cc WRITE, profiles READ") if Param("shadowdb"); # Add the bug report to the DB. SendSQL($sql); @@ -291,6 +295,12 @@ SendSQL($sql); SendSQL("select LAST_INSERT_ID()"); my $id = FetchOneColumn(); +# Add the group restrictions +foreach my $grouptoadd (@groupstoadd) { + SendSQL("INSERT INTO bug_group_map (bug_id, group_id) + VALUES ($id, $grouptoadd)"); +} + # Add the comment SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext) VALUES ($id, $::userid, now(), " . SqlQuote($comment) . ")"); diff --git a/process_bug.cgi b/process_bug.cgi index f62285ffb..3468a9790 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -47,7 +47,6 @@ use vars qw(%versions %settable_resolution %target_milestone %legal_severity - %superusergroupset $next_bug); ConnectToDatabase(); @@ -143,7 +142,7 @@ if ( Param("usetargetmilestone") ) { # # This function checks if there is a comment required for a specific # function and tests, if the comment was given. -# If comments are required for functions is defined by params. +# If comments are required for functions is defined by params. # sub CheckonComment( $ ) { my ($function) = (@_); @@ -410,10 +409,8 @@ sub DuplicateUserConfirm { SendSQL("SELECT reporter FROM bugs WHERE bug_id = " . SqlQuote($dupe)); my $reporter = FetchOneColumn(); - SendSQL("SELECT profiles.groupset FROM profiles WHERE profiles.userid =".SqlQuote($reporter)); - my $reportergroupset = FetchOneColumn(); - if (CanSeeBug($original, $reporter, $reportergroupset)) { + if (CanSeeBug($original, $reporter)) { $::FORM{'confirm_add_duplicate'} = "1"; return; } @@ -460,9 +457,9 @@ if (defined $::FORM{'id'}) { CheckFormFieldDefined(\%::FORM, 'longdesclength'); } -my $action = ''; +my $action = ''; if (defined $::FORM{action}) { - $action = trim($::FORM{action}); + $action = trim($::FORM{action}); } if (Param("move-enabled") && $action eq Param("move-button-text")) { $::FORM{'buglist'} = join (":", @idlist); @@ -564,33 +561,27 @@ sub ChangeResolution { # operations # If the form element isn't present, or the user isn't in the group, leave # it as-is -if($::usergroupset ne '0') { - my $groupAdd = "0"; - my $groupDel = "0"; - - SendSQL("SELECT bit, isactive FROM groups WHERE " . - "isbuggroup != 0 AND bit & $::usergroupset != 0 ORDER BY bit"); - while (my ($b, $isactive) = FetchSQLData()) { - # The multiple change page may not show all groups a bug is in - # (eg product groups when listing more than one product) - # Only consider groups which were present on the form. We can't do this - # for single bug changes because non-checked checkboxes aren't present. - # All the checkboxes should be shown in that case, though, so its not - # an issue there - if ($::FORM{'id'} || exists $::FORM{"bit-$b"}) { - if (!$::FORM{"bit-$b"}) { - $groupDel .= "+$b"; - } elsif ($::FORM{"bit-$b"} == 1 && $isactive) { - $groupAdd .= "+$b"; - } +my @groupAdd = (); +my @groupDel = (); + +SendSQL("SELECT groups.id, isactive FROM groups, user_group_map WHERE " . + "groups.id = user_group_map.group_id AND " . + "user_group_map.user_id = $::userid AND " . + "isbless = 0 AND isbuggroup = 1"); +while (my ($b, $isactive) = FetchSQLData()) { + # The multiple change page may not show all groups a bug is in + # (eg product groups when listing more than one product) + # Only consider groups which were present on the form. We can't do this + # for single bug changes because non-checked checkboxes aren't present. + # All the checkboxes should be shown in that case, though, so its not + # an issue there + if ($::FORM{'id'} || exists $::FORM{"bit-$b"}) { + if (!$::FORM{"bit-$b"}) { + push(@groupDel, $b); + } elsif ($::FORM{"bit-$b"} == 1 && $isactive) { + push(@groupAdd, $b); } } - if ($groupAdd ne "0" || $groupDel ne "0") { - DoComma(); - # mysql < 3.23.5 doesn't support the ~ operator, even though - # the docs say that it does - $::query .= "groupset = ((groupset & ($::superusergroupset - ($groupDel))) | ($groupAdd))"; - } } foreach my $field ("rep_platform", "priority", "bug_severity", @@ -708,9 +699,9 @@ if (defined $::FORM{'qa_contact'}) { # and cc list can see the bug even if they are not members of all groups # to which the bug is restricted. if ( $::FORM{'id'} ) { - SendSQL("SELECT groupset FROM bugs WHERE bug_id = $::FORM{'id'}"); - my ($groupset) = FetchSQLData(); - if ( $groupset ) { + SendSQL("SELECT group_id FROM bug_group_map WHERE bug_id = $::FORM{'id'}"); + my ($havegroup) = FetchSQLData(); + if ( $havegroup ) { DoComma(); $::FORM{'reporter_accessible'} = $::FORM{'reporter_accessible'} ? '1' : '0'; $::query .= "reporter_accessible = $::FORM{'reporter_accessible'}"; @@ -1047,6 +1038,8 @@ foreach my $id (@idlist) { "profiles $write, dependencies $write, votes $write, " . "products READ, components READ, " . "keywords $write, longdescs $write, fielddefs $write, " . + "bug_group_map $write, " . + "user_group_map READ, " . "keyworddefs READ, groups READ, attachments READ"); my @oldvalues = SnapShotBug($id); my %oldhash; @@ -1206,9 +1199,29 @@ foreach my $id (@idlist) { if ($::comma ne "") { SendSQL($query); } + my @groupAddNames = (); + foreach my $grouptoadd (@groupAdd) { + if (!BugInGroupId($id, $grouptoadd)) { + push(@groupAddNames, GroupIdToName($grouptoadd)); + SendSQL("INSERT INTO bug_group_map (bug_id, group_id) + VALUES ($id, $grouptoadd)"); + } + } + my @groupDelNames = (); + foreach my $grouptodel (@groupDel) { + if (BugInGroupId($id, $grouptodel)) { + push(@groupDelNames, GroupIdToName($grouptodel)); + } + SendSQL("DELETE FROM bug_group_map + WHERE bug_id = $id AND group_id = $grouptodel"); + } SendSQL("select now()"); $timestamp = FetchOneColumn(); - + + my $groupDelNames = join(',', @groupDelNames); + my $groupAddNames = join(',', @groupAddNames); + + LogActivityEntry($id, "bug_group", $groupDelNames, $groupAddNames); if (defined $::FORM{'comment'}) { AppendComment($id, $::COOKIE{'Bugzilla_login'}, $::FORM{'comment'}, $::FORM{'commentprivacy'}); @@ -1322,7 +1335,7 @@ foreach my $id (@idlist) { # the user wants to add the bug to the new product's group; ($::FORM{'addtonewgroup'} eq 'yes' || ($::FORM{'addtonewgroup'} eq 'yesifinold' - && GroupNameToBit($oldhash{'product'}) & $oldhash{'groupset'})) + && BugInGroup($id, $oldhash{'product'}))) # the new product is associated with a group; && GroupExists($::FORM{'product'}) @@ -1344,14 +1357,16 @@ foreach my $id (@idlist) { && (UserInGroup($::FORM{'product'}) || !Param('usebuggroupsentry')) # the associated group is active, indicating it can accept new bugs; - && GroupIsActive(GroupNameToBit($::FORM{'product'})) + && GroupIsActive(GroupNameToId($::FORM{'product'})) ) { # Add the bug to the group associated with its new product. - my $groupbit = GroupNameToBit($::FORM{'product'}); - SendSQL("UPDATE bugs SET groupset = groupset + $groupbit WHERE bug_id = $id"); + my $groupid = GroupNameToId($::FORM{'product'}); + if (!BugInGroupId($id, $groupid)) { + SendSQL("INSERT INTO bug_group_map (bug_id, group_id) VALUES ($id, $groupid)"); + } } - if ( + if ( # the old product is associated with a group; GroupExists($oldhash{'product'}) @@ -1359,8 +1374,8 @@ foreach my $id (@idlist) { && BugInGroup($id, $oldhash{'product'}) ) { # Remove the bug from the group associated with its old product. - my $groupbit = GroupNameToBit($oldhash{'product'}); - SendSQL("UPDATE bugs SET groupset = groupset - $groupbit WHERE bug_id = $id"); + my $groupid = GroupNameToId($oldhash{'product'}); + SendSQL("DELETE FROM bug_group_map WHERE bug_id = $id AND group_id = $groupid"); } } @@ -1523,7 +1538,7 @@ if ($::COOKIE{"BUGLIST"} && $::FORM{'id'}) { 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)) { + if (detaint_natural($next_bug) && CanSeeBug($next_bug, $::userid)) { $::FORM{'id'} = $next_bug; $vars->{'next_id'} = $next_bug; diff --git a/processmail b/processmail index 45aaacc77..a47297597 100755 --- a/processmail +++ b/processmail @@ -632,14 +632,17 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { } - SendSQL("SELECT userid, groupset " . + SendSQL("SELECT userid, (refreshed_when > " . SqlQuote($::last_changed) . ") " . "FROM profiles WHERE login_name = " . SqlQuote($person)); - my ($userid, $groupset) = (FetchSQLData()); + my ($userid, $current) = (FetchSQLData()); $seen{$person} = 1; detaint_natural($userid); - detaint_natural($groupset); + + if (!$current) { + DeriveGroup($userid); + } # if this person doesn't have permission to see info on this bug, # return. @@ -649,19 +652,13 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { # see the action of restricting the bug itself; the bug will just # quietly disappear from their radar. # - return unless CanSeeBug($id, $userid, $groupset); + return unless CanSeeBug($id, $userid); + # Drop any non-insiders if the comment is private - if (Param("insidergroup") && ($anyprivate != 0)) { - ConnectToDatabase(); - PushGlobalSQLState(); - SendSQL("select (bit & $groupset ) != 0 from groups where name = " . SqlQuote(Param("insidergroup"))); - my $bit = FetchOneColumn(); - PopGlobalSQLState(); - if (!$bit) { - return; - } - } + return if (Param("insidergroup") && + ($anyprivate != 0) && + (!UserInGroup(Param("insidergroup"), $userid))); # We shouldn't send changedmail if this is a dependency mail, and any of # the depending bugs is not visible to the user. @@ -669,7 +666,7 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { my $save_id = $dep_id; detaint_natural($dep_id) || warn("Unexpected Error: \@depbugs contains a non-numeric value: '$save_id'") && return; - return unless CanSeeBug($dep_id, $userid, $groupset); + return unless CanSeeBug($dep_id, $userid); } my %mailhead = %defmailhead; @@ -781,6 +778,14 @@ if (open(FID, "= 0 && $ARGV[0] eq "regenerate") { print "Regenerating is no longer required or supported\n"; exit; diff --git a/query.cgi b/query.cgi index e038d98ae..cd679e8e8 100755 --- a/query.cgi +++ b/query.cgi @@ -50,19 +50,19 @@ use vars qw( ); ConnectToDatabase(); - +my $userid = 0; if (defined $::FORM{"GoAheadAndLogIn"}) { # We got here from a login page, probably from relogin.cgi. We better # make sure the password is legit. - confirm_login(); + $userid = confirm_login(); } else { - quietly_check_login(); + $userid = quietly_check_login(); } # Backwards compatibility hack -- if there are any of the old QUERY_* # cookies around, and we are logged in, then move them into the database # and nuke the cookie. This is required for Bugzilla 2.8 and earlier. -if ($::userid) { +if ($userid) { my @oldquerycookies; foreach my $i (keys %::COOKIE) { if ($i =~ /^QUERY_(.*)$/) { @@ -79,12 +79,12 @@ if ($::userid) { if ($value) { my $qname = SqlQuote($name); SendSQL("SELECT query FROM namedqueries " . - "WHERE userid = $::userid AND name = $qname"); + "WHERE userid = $userid AND name = $qname"); my $query = FetchOneColumn(); if (!$query) { SendSQL("REPLACE INTO namedqueries " . "(userid, name, query) VALUES " . - "($::userid, $qname, " . SqlQuote($value) . ")"); + "($userid, $qname, " . SqlQuote($value) . ")"); } } print "Set-Cookie: $cookiename= ; path=" . Param("cookiepath") . @@ -94,17 +94,17 @@ if ($::userid) { } if ($::FORM{'nukedefaultquery'}) { - if ($::userid) { + if ($userid) { SendSQL("DELETE FROM namedqueries " . - "WHERE userid = $::userid AND name = '$::defaultqueryname'"); + "WHERE userid = $userid AND name = '$::defaultqueryname'"); } $::buffer = ""; } my $userdefaultquery; -if ($::userid) { +if ($userid) { SendSQL("SELECT query FROM namedqueries " . - "WHERE userid = $::userid AND name = '$::defaultqueryname'"); + "WHERE userid = $userid AND name = '$::defaultqueryname'"); $userdefaultquery = FetchOneColumn(); } @@ -285,7 +285,7 @@ $vars->{'rep_platform'} = \@::legal_platform; $vars->{'op_sys'} = \@::legal_opsys; $vars->{'priority'} = \@::legal_priority; $vars->{'bug_severity'} = \@::legal_severity; -$vars->{'userid'} = $::userid; +$vars->{'userid'} = $userid; # Boolean charts my @fields; @@ -332,10 +332,10 @@ for (my $chart = 0; $::FORM{"field$chart-0-0"}; $chart++) { $default{'charts'} = \@charts; # Named queries -if ($::userid) { +if ($userid) { my @namedqueries; SendSQL("SELECT name FROM namedqueries " . - "WHERE userid = $::userid AND name != '$::defaultqueryname' " . + "WHERE userid = $userid AND name != '$::defaultqueryname' " . "ORDER BY name"); while (MoreSQLData()) { push(@namedqueries, FetchOneColumn()); diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 451ad23da..da71163cc 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -109,6 +109,17 @@ sub CrossCheck { } } +sub DateCheck { + my $table = shift @_; + my $field = shift @_; + Status("Checking dates in $table.$field"); + SendSQL("SELECT COUNT( $field ) FROM $table WHERE $field > NOW()"); + my $c = FetchOneColumn(); + if ($c) { + Alert("Found $c dates in future"); + } +} + my @badbugs; @@ -139,6 +150,57 @@ if (exists $::FORM{'rebuildvotecache'}) { Status("Vote cache has been rebuilt."); } +if (exists $::FORM{'rederivegroups'}) { + Status("OK, All users' inherited permissions will be rechecked when " . + "they next access Bugzilla."); + SendSQL("UPDATE groups SET last_changed = NOW() LIMIT 1"); +} + +# rederivegroupsnow is REALLY only for testing. +if (exists $::FORM{'rederivegroupsnow'}) { + Status("OK, now rederiving groups."); + SendSQL("SELECT userid FROM profiles"); + while ((my $id) = FetchSQLData()) { + DeriveGroup($id); + Status("Group $id"); + } +} + +if (exists $::FORM{'cleangroupsnow'}) { + Status("OK, now cleaning stale groups."); + # Only users that were out of date already long ago should be cleaned + # and the cleaning is done with tables locked. This is require in order + # to keep another session from proceeding with permission checks + # after the groups have been cleaned unless it first had an opportunity + # to get the groups up to date. + # If any page starts taking longer than one hour to load, this interval + # should be revised. + SendSQL("SELECT MAX(last_changed) FROM groups WHERE last_changed < NOW() - INTERVAL 1 HOUR"); + (my $cutoff) = FetchSQLData(); + Status("Cutoff is $cutoff"); + SendSQL("SELECT COUNT(*) FROM user_group_map"); + (my $before) = FetchSQLData(); + SendSQL("LOCK TABLES user_group_map WRITE, profiles WRITE"); + SendSQL("SELECT userid FROM profiles " . + "WHERE refreshed_when > 0 " . + "AND refreshed_when < " . SqlQuote($cutoff) . + " LIMIT 1000"); + my $count = 0; + while ((my $id) = FetchSQLData()) { + $count++; + PushGlobalSQLState(); + SendSQL("DELETE FROM user_group_map WHERE " . + "user_id = $id AND isderived = 1 AND isbless = 0"); + SendSQL("UPDATE profiles SET refreshed_when = 0 WHERE userid = $id"); + PopGlobalSQLState(); + } + SendSQL("UNLOCK TABLES"); + SendSQL("SELECT COUNT(*) FROM user_group_map"); + (my $after) = FetchSQLData(); + Status("Cleaned table for $count users " . + "- reduced from $before records to $after records"); +} + print "OK, now running sanity checks.

\n"; # This one goes first, because if this is wrong, then the below tests @@ -178,6 +240,7 @@ CrossCheck("attachstatusdefs", "id", CrossCheck("bugs", "bug_id", ["bugs_activity", "bug_id"], + ["bug_group_map", "bug_id"], ["attachments", "bug_id"], ["cc", "bug_id"], ["longdescs", "bug_id"], @@ -188,6 +251,12 @@ CrossCheck("bugs", "bug_id", ["duplicates", "dupe_of", "dupe"], ["duplicates", "dupe", "dupe_of"]); +CrossCheck("groups", "id", + ["bug_group_map", "group_id"], + ["group_group_map", "grantor_id"], + ["group_group_map", "member_id"], + ["user_group_map", "group_id"]); + CrossCheck("profiles", "userid", ["bugs", "reporter", "bug_id"], ["bugs", "assigned_to", "bug_id"], @@ -203,6 +272,7 @@ CrossCheck("profiles", "userid", ["watch", "watched"], ["tokens", "userid"], ["components", "initialowner", "name"], + ["user_group_map", "user_id"], ["components", "initialqacontact", "name", ["0"]]); CrossCheck("products", "id", @@ -212,25 +282,9 @@ CrossCheck("products", "id", ["versions", "product_id", "value"], ["attachstatusdefs", "product_id", "name"]); -########################################################################### -# Perform group checks -########################################################################### +DateCheck("groups", "last_changed"); +DateCheck("profiles", "refreshed_when"); -Status("Checking groups"); -SendSQL("select bit from groups where bit != pow(2, round(log(bit) / log(2)))"); -while (my $bit = FetchOneColumn()) { - Alert("Illegal bit number found in group table: $bit"); -} - -SendSQL("select sum(bit) from groups where isbuggroup != 0"); -my $buggroupset = FetchOneColumn(); -if (!defined $buggroupset || $buggroupset eq "") { - $buggroupset = 0; -} -SendSQL("select bug_id, groupset from bugs where groupset & $buggroupset != groupset"); -while (@row = FetchSQLData()) { - Alert("Bad groupset $row[1] found in bug " . BugLink($row[0])); -} ########################################################################### # Perform product specific field checks diff --git a/show_activity.cgi b/show_activity.cgi index f6d9b75c9..14b4149ba 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -35,10 +35,7 @@ ConnectToDatabase(); # Begin Data/Security Validation ############################################################################### -# Check whether or not the user is currently logged in. This function -# sets the value of $::usergroupset, the binary number that records -# the set of groups to which the user belongs and which we can use -# to determine whether or not the user is authorized to access this bug. +# Check whether or not the user is currently logged in. quietly_check_login(); # Make sure the bug ID is a positive integer representing an existing diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index e5885d180..021150bf0 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -31,7 +31,7 @@ ConnectToDatabase(); quietly_check_login(); -use vars qw($template $vars $userid $usergroupset); +use vars qw($template $vars $userid); my %seen; my %edgesdone; @@ -128,13 +128,13 @@ foreach my $k (keys(%seen)) { my $summary = ""; my $stat; if ($::FORM{'showsummary'}) { - SendSQL(SelectVisible("SELECT bug_status, short_desc FROM bugs " . - "WHERE bugs.bug_id = $k", - $::userid, - $::usergroupset)); - ($stat, $summary) = FetchSQLData(); - $stat = "NEW" if !defined $stat; - $summary = "" if !defined $summary; + if (CanSeeBug($k, $::userid)) { + SendSQL("SELECT bug_status, short_desc FROM bugs " . + "WHERE bugs.bug_id = $k"); + ($stat, $summary) = FetchSQLData(); + $stat = "NEW" if !defined $stat; + $summary = "" if !defined $summary; + } } else { SendSQL("SELECT bug_status FROM bugs WHERE bug_id = $k"); $stat = FetchOneColumn(); diff --git a/showdependencytree.cgi b/showdependencytree.cgi index 7917f00fe..e2e9d52b0 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -39,7 +39,6 @@ quietly_check_login(); # More warning suppression silliness. $::userid = $::userid; -$::usergroupset = $::usergroupset; ################################################################################ # Data/Security Validation # @@ -144,7 +143,9 @@ sub GetBug { # and returns it to the calling code. my ($id) = @_; - SendSQL(SelectVisible("SELECT 1, + my $bug = {}; + if (CanSeeBug($id, $::userid)) { + SendSQL("SELECT 1, bug_status, short_desc, $milestone_column, @@ -152,18 +153,16 @@ sub GetBug { assignee.login_name FROM bugs, profiles AS assignee WHERE bugs.bug_id = $id - AND bugs.assigned_to = assignee.userid", - $::userid, - $::usergroupset)); + AND bugs.assigned_to = assignee.userid"); - my $bug = {}; - ($bug->{'exists'}, - $bug->{'status'}, - $bug->{'summary'}, - $bug->{'milestone'}, - $bug->{'assignee_id'}, - $bug->{'assignee_email'}) = FetchSQLData(); + ($bug->{'exists'}, + $bug->{'status'}, + $bug->{'summary'}, + $bug->{'milestone'}, + $bug->{'assignee_id'}, + $bug->{'assignee_email'}) = FetchSQLData(); + } $bug->{'open'} = IsOpenedState($bug->{'status'}); $bug->{'dependencies'} = []; diff --git a/sidebar.cgi b/sidebar.cgi index d3692b16d..ec021ea1c 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -36,11 +36,11 @@ quietly_check_login(); $vars->{'username'} = $::COOKIE{'Bugzilla_login'} || ''; if (defined $::COOKIE{'Bugzilla_login'}) { - SendSQL("SELECT mybugslink, userid, blessgroupset FROM profiles " . + SendSQL("SELECT mybugslink, userid FROM profiles " . "WHERE login_name = " . SqlQuote($::COOKIE{'Bugzilla_login'})); - my ($mybugslink, $userid, $blessgroupset) = (FetchSQLData()); + my ($mybugslink, $userid) = (FetchSQLData()); $vars->{'userid'} = $userid; - $vars->{'blessgroupset'} = $blessgroupset; + $vars->{'canblessanything'} = UserCanBlessAnything(); if ($mybugslink) { my $mybugstemplate = Param("mybugstemplate"); my %substs = ( 'userid' => url_quote($::COOKIE{'Bugzilla_login'}) ); diff --git a/template/en/default/account/prefs/permissions.html.tmpl b/template/en/default/account/prefs/permissions.html.tmpl index 15bca6deb..064220d6e 100644 --- a/template/en/default/account/prefs/permissions.html.tmpl +++ b/template/en/default/account/prefs/permissions.html.tmpl @@ -20,36 +20,48 @@ #%] [%# INTERFACE: - # has_bits: array of strings. May be empty. - # Descriptions of the permission bits the user has. - # set_bits: array of strings. May be empty. - # Descriptions of the permission bits the user can set for + # has_bits: array of hashes. May be empty. + # name => Names of the permissions the user has. + # desc => Descriptions of the permissions the user has. + # set_bits: array of hashes. May be empty. + # name => Names of the permissions the user can set for + # other people. + # desc => Descriptions of the permissions the user can set for # other people. #%] - +
diff --git a/template/en/default/global/useful-links.html.tmpl b/template/en/default/global/useful-links.html.tmpl index 987ca370b..1e5b09df1 100644 --- a/template/en/default/global/useful-links.html.tmpl +++ b/template/en/default/global/useful-links.html.tmpl @@ -65,7 +65,7 @@ [% ', parameters' IF user.groups.tweakparams %] [% ', users' IF user.groups.editusers - || (user.blessgroupset > 0) %] + || user.canblessany %] [% ', products' IF user.groups.editcomponents %] [% ', attachment statuses' diff --git a/template/en/default/sidebar.xul.tmpl b/template/en/default/sidebar.xul.tmpl index ad7158b13..21a07c998 100644 --- a/template/en/default/sidebar.xul.tmpl +++ b/template/en/default/sidebar.xul.tmpl @@ -73,7 +73,7 @@ function normal_keypress_handler( aEvent ) { [%- IF UserInGroup('tweakparams') %] [%- END %] - [%- IF UserInGroup('editusers') || blessgroupset %] + [%- IF UserInGroup('editusers') || canblessany %] [%- END %] [%- IF UserInGroup('editcomponents') %] diff --git a/token.cgi b/token.cgi index 43ee1b04e..86e51939d 100755 --- a/token.cgi +++ b/token.cgi @@ -265,6 +265,7 @@ sub changeEmail { SendSQL("DELETE FROM tokens WHERE userid = $userid AND tokentype = 'emailnew'"); SendSQL("UNLOCK TABLES"); + DeriveGroup($userid); # Return HTTP response headers. print "Content-Type: text/html\n\n"; @@ -300,6 +301,7 @@ sub cancelChangeEmail { SET login_name = $quotedoldemail WHERE userid = $userid"); SendSQL("UNLOCK TABLES"); + DeriveGroup($userid); $vars->{'message'} .= " Your old account settings have been reinstated."; } diff --git a/userprefs.cgi b/userprefs.cgi index 808aebf40..369c681ca 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -33,7 +33,6 @@ use RelationSet; sub sillyness { my $zz; $zz = $::defaultqueryname; - $zz = $::usergroupset; } # Use global template variables. @@ -331,21 +330,22 @@ sub SaveFooter { sub DoPermissions { my (@has_bits, @set_bits); - SendSQL("SELECT description FROM groups " . - "WHERE bit & $::usergroupset != 0 " . - "ORDER BY bit"); + SendSQL("SELECT DISTINCT name, description FROM groups, user_group_map " . + "WHERE user_group_map.group_id = groups.id " . + "AND user_id = $::userid " . + "AND isbless = 0 " . + "ORDER BY name"); while (MoreSQLData()) { - push(@has_bits, FetchSQLData()); + my ($nam, $desc) = FetchSQLData(); + push(@has_bits, {"desc" => $desc, "name" => $nam}); } - - SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $userid"); - my $blessgroupset = FetchOneColumn(); - if ($blessgroupset) { - SendSQL("SELECT description FROM groups " . - "WHERE bit & $blessgroupset != 0 " . - "ORDER BY bit"); - while (MoreSQLData()) { - push(@set_bits, FetchSQLData()); + my @set_ids = (); + SendSQL("SELECT DISTINCT name, description FROM groups " . + "ORDER BY name"); + while (MoreSQLData()) { + my ($nam, $desc) = FetchSQLData(); + if (UserCanBlessGroup($nam)) { + push(@set_bits, {"desc" => $desc, "name" => $nam}); } } diff --git a/votes.cgi b/votes.cgi index 88e303971..3bfe11682 100755 --- a/votes.cgi +++ b/votes.cgi @@ -28,7 +28,6 @@ use lib "."; require "CGI.pl"; -use vars qw($usergroupset); # Use global template variables use vars qw($template $vars); @@ -188,7 +187,7 @@ sub show_user { # and they can see there are votes 'missing', but not on what bug # they are. This seems a reasonable compromise; the alternative is # to lie in the totals. - next if !CanSeeBug($id, $who, $usergroupset); + next if !CanSeeBug($id, $who); push (@bugs, { id => $id, summary => $summary, -- cgit v1.2.3-65-gdbad From 90975fe914d066726d06f53abe8696399b13a61a Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Sat, 28 Sep 2002 11:10:27 +0000 Subject: Fix for bug 171296: changing Content-disposition header in attachment.cgi to use 'inline' instead of 'attachment' so that it doesn't *force* you to download it. r= bbaetz, bzbarsky --- attachment.cgi | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index cfdbd4ea6..4d5fea475 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -342,17 +342,11 @@ sub view SendSQL("SELECT mimetype, filename, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); my ($contenttype, $filename, $thedata) = FetchSQLData(); - # Determine if the browser supports the Content-Disposition header or not - my $usedisposition = 1; # assume yes, unless we discover otherwise - if ($::ENV{HTTP_USER_AGENT} =~ /^Mozilla.*MSIE (\d+)/) { - if ($1 < 5) { $usedisposition = 0; } # MSIE < 5.0 chokes on it - } - # Return the appropriate HTTP response headers. $filename =~ s/^.*[\/\\]//; my $filesize = length($thedata); print qq{Content-Type: $contenttype; name="$filename"\n}; - print qq{Content-Disposition: attachment; filename=$filename\n} if $usedisposition; + print qq{Content-Disposition: inline; filename=$filename\n}; print qq{Content-Length: $filesize\n}; print qq{\n$thedata}; -- cgit v1.2.3-65-gdbad From 91b171e7584920d03abb9c45e779c84f3dee975c Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Sun, 29 Sep 2002 01:42:23 +0000 Subject: Fix for bug 98801: Implementation of the request tracker, a set of enhancements to attachment statuses. r=gerv,bbaetz --- Attachment.pm | 42 +- Bugzilla/Attachment.pm | 42 +- Bugzilla/Flag.pm | 591 +++++++++++++++++++++ Bugzilla/FlagType.pm | 325 +++++++++++ Bugzilla/Search.pm | 128 ++--- Bugzilla/User.pm | 176 ++++++ attachment.cgi | 188 ++----- bug_form.pl | 46 +- checksetup.pl | 206 ++++++- editattachstatuses.cgi | 347 ------------ editcomponents.cgi | 10 +- editflagtypes.cgi | 494 +++++++++++++++++ editproducts.cgi | 10 +- globals.pl | 12 +- process_bug.cgi | 29 +- productmenu.js | 242 +++++++++ request.cgi | 279 ++++++++++ sanitycheck.cgi | 8 +- template/en/default/account/prefs/email.html.tmpl | 20 +- .../admin/flag-type/confirm-delete.html.tmpl | 58 ++ template/en/default/admin/flag-type/edit.html.tmpl | 189 +++++++ template/en/default/admin/flag-type/list.html.tmpl | 107 ++++ template/en/default/attachment/edit.html.tmpl | 21 +- template/en/default/attachment/list.html.tmpl | 35 +- template/en/default/bug/edit.html.tmpl | 20 +- template/en/default/flag/list.html.tmpl | 94 ++++ template/en/default/global/code-error.html.tmpl | 42 ++ template/en/default/global/messages.html.tmpl | 28 + template/en/default/global/select-menu.html.tmpl | 12 +- template/en/default/global/useful-links.html.tmpl | 4 +- template/en/default/global/user-error.html.tmpl | 39 +- template/en/default/request/created-email.txt.tmpl | 41 ++ .../en/default/request/fulfilled-email.txt.tmpl | 42 ++ template/en/default/request/queue.html.tmpl | 193 +++++++ template/en/default/request/verify.html.tmpl | 108 ++++ userprefs.cgi | 9 + 36 files changed, 3562 insertions(+), 675 deletions(-) create mode 100644 Bugzilla/Flag.pm create mode 100644 Bugzilla/FlagType.pm create mode 100644 Bugzilla/User.pm delete mode 100755 editattachstatuses.cgi create mode 100755 editflagtypes.cgi create mode 100644 productmenu.js create mode 100755 request.cgi create mode 100644 template/en/default/admin/flag-type/confirm-delete.html.tmpl create mode 100644 template/en/default/admin/flag-type/edit.html.tmpl create mode 100644 template/en/default/admin/flag-type/list.html.tmpl create mode 100644 template/en/default/flag/list.html.tmpl create mode 100644 template/en/default/request/created-email.txt.tmpl create mode 100644 template/en/default/request/fulfilled-email.txt.tmpl create mode 100644 template/en/default/request/queue.html.tmpl create mode 100644 template/en/default/request/verify.html.tmpl (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 3a6248cf4..53690170e 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -31,10 +31,32 @@ package Attachment; # This module requires that its caller have said "require CGI.pl" to import # relevant functions from that script and its companion globals.pl. +# Use the Flag module to handle flags. +use Bugzilla::Flag; + ############################################################################ # Functions ############################################################################ +sub new { + # Returns a hash of information about the attachment with the given ID. + + my ($invocant, $id) = @_; + return undef if !$id; + my $self = { 'id' => $id }; + my $class = ref($invocant) || $invocant; + bless($self, $class); + + &::PushGlobalSQLState(); + &::SendSQL("SELECT 1, description, bug_id FROM attachments " . + "WHERE attach_id = $id"); + ($self->{'exists'}, $self->{'summary'}, $self->{'bug_id'}) = + &::FetchSQLData(); + &::PopGlobalSQLState(); + + return $self; +} + sub query { # Retrieves and returns an array of attachment records for a given bug. @@ -65,23 +87,9 @@ sub query $a{'date'} = "$1-$2-$3 $4:$5"; } - # Retrieve a list of status flags that have been set on the attachment. - &::PushGlobalSQLState(); - &::SendSQL(" - SELECT name - FROM attachstatuses, attachstatusdefs - WHERE attach_id = $a{'attachid'} - AND attachstatuses.statusid = attachstatusdefs.id - ORDER BY sortkey - "); - my @statuses = (); - while (&::MoreSQLData()) { - my ($status) = &::FetchSQLData(); - push @statuses , $status; - } - $a{'statuses'} = \@statuses; - &::PopGlobalSQLState(); - + # Retrieve a list of flags for this attachment. + $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); + # We will display the edit link if the user can edit the attachment; # ie the are the submitter, or they have canedit. # Also show the link if the user is not logged in - in that cae, diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 3a6248cf4..53690170e 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -31,10 +31,32 @@ package Attachment; # This module requires that its caller have said "require CGI.pl" to import # relevant functions from that script and its companion globals.pl. +# Use the Flag module to handle flags. +use Bugzilla::Flag; + ############################################################################ # Functions ############################################################################ +sub new { + # Returns a hash of information about the attachment with the given ID. + + my ($invocant, $id) = @_; + return undef if !$id; + my $self = { 'id' => $id }; + my $class = ref($invocant) || $invocant; + bless($self, $class); + + &::PushGlobalSQLState(); + &::SendSQL("SELECT 1, description, bug_id FROM attachments " . + "WHERE attach_id = $id"); + ($self->{'exists'}, $self->{'summary'}, $self->{'bug_id'}) = + &::FetchSQLData(); + &::PopGlobalSQLState(); + + return $self; +} + sub query { # Retrieves and returns an array of attachment records for a given bug. @@ -65,23 +87,9 @@ sub query $a{'date'} = "$1-$2-$3 $4:$5"; } - # Retrieve a list of status flags that have been set on the attachment. - &::PushGlobalSQLState(); - &::SendSQL(" - SELECT name - FROM attachstatuses, attachstatusdefs - WHERE attach_id = $a{'attachid'} - AND attachstatuses.statusid = attachstatusdefs.id - ORDER BY sortkey - "); - my @statuses = (); - while (&::MoreSQLData()) { - my ($status) = &::FetchSQLData(); - push @statuses , $status; - } - $a{'statuses'} = \@statuses; - &::PopGlobalSQLState(); - + # Retrieve a list of flags for this attachment. + $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); + # We will display the edit link if the user can edit the attachment; # ie the are the submitter, or they have canedit. # Also show the link if the user is not logged in - in that cae, diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm new file mode 100644 index 000000000..3feaae4cd --- /dev/null +++ b/Bugzilla/Flag.pm @@ -0,0 +1,591 @@ +# -*- 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): Myk Melez + +################################################################################ +# Module Initialization +################################################################################ + +# Make it harder for us to do dangerous things in Perl. +use strict; + +# This module implements bug and attachment flags. +package Bugzilla::Flag; + +use Bugzilla::FlagType; +use Bugzilla::User; +use Attachment; + +use vars qw($template $vars); + +# Note! This module requires that its caller have said "require CGI.pl" +# to import relevant functions from that script and its companion globals.pl. + +################################################################################ +# Global Variables +################################################################################ + +# basic sets of columns and tables for getting flags from the database + +my @base_columns = + ("1", "id", "type_id", "bug_id", "attach_id", "requestee_id", "setter_id", + "status"); + +# Note: when adding tables to @base_tables, make sure to include the separator +# (i.e. a comma or words like "LEFT OUTER JOIN") before the table name, +# since tables take multiple separators based on the join type, and therefore +# it is not possible to join them later using a single known separator. + +my @base_tables = ("flags"); + +################################################################################ +# Searching/Retrieving Flags +################################################################################ + +# !!! Implement a cache for this function! +sub get { + # Retrieves and returns a flag from the database. + + my ($id) = @_; + + my $select_clause = "SELECT " . join(", ", @base_columns); + my $from_clause = "FROM " . join(" ", @base_tables); + + # Execute the query, retrieve the result, and write it into a record. + &::PushGlobalSQLState(); + &::SendSQL("$select_clause $from_clause WHERE flags.id = $id"); + my $flag = perlify_record(&::FetchSQLData()); + &::PopGlobalSQLState(); + + return $flag; +} + +sub match { + # Queries the database for flags matching the given criteria + # (specified as a hash of field names and their matching values) + # and returns an array of matching records. + + my ($criteria) = @_; + + my $select_clause = "SELECT " . join(", ", @base_columns); + my $from_clause = "FROM " . join(" ", @base_tables); + + my @criteria = sqlify_criteria($criteria); + + my $where_clause = "WHERE " . join(" AND ", @criteria); + + # Execute the query, retrieve the results, and write them into records. + &::PushGlobalSQLState(); + &::SendSQL("$select_clause $from_clause $where_clause"); + my @flags; + while (&::MoreSQLData()) { + my $flag = perlify_record(&::FetchSQLData()); + push(@flags, $flag); + } + &::PopGlobalSQLState(); + + return \@flags; +} + +sub count { + # Queries the database for flags matching the given criteria + # (specified as a hash of field names and their matching values) + # and returns an array of matching records. + + my ($criteria) = @_; + + my @criteria = sqlify_criteria($criteria); + + my $where_clause = "WHERE " . join(" AND ", @criteria); + + # Execute the query, retrieve the result, and write it into a record. + &::PushGlobalSQLState(); + &::SendSQL("SELECT COUNT(id) FROM flags $where_clause"); + my $count = &::FetchOneColumn(); + &::PopGlobalSQLState(); + + return $count; +} + +################################################################################ +# Creating and Modifying +################################################################################ + +sub validate { + # Validates fields containing flag modifications. + + my ($data) = @_; + + # Get a list of flags to validate. Uses the "map" function + # to extract flag IDs from form field names by matching fields + # whose name looks like "flag-nnn", where "nnn" is the ID, + # and returning just the ID portion of matching field names. + my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data); + + foreach my $id (@ids) + { + my $status = $data->{"flag-$id"}; + + # Make sure the flag exists. + my $flag = get($id); + $flag || &::ThrowCodeError("flag_nonexistent", { id => $id }); + + # Don't bother validating flags the user didn't change. + next if $status eq $flag->{'status'}; + + # Make sure the user chose a valid status. + grep($status eq $_, qw(X + - ?)) + || &::ThrowCodeError("flag_status_invalid", + { id => $id , status => $status }); + } +} + +sub process { + # Processes changes to flags. + + # The target is the bug or attachment this flag is about, the timestamp + # is the date/time the bug was last touched (so that changes to the flag + # can be stamped with the same date/time), the data is the form data + # with flag fields that the user submitted, the old bug is the bug record + # before the user made changes to it, and the new bug is the bug record + # after the user made changes to it. + + my ($target, $timestamp, $data, $oldbug, $newbug) = @_; + + # Use the date/time we were given if possible (allowing calling code + # to synchronize the comment's timestamp with those of other records). + $timestamp = ($timestamp ? &::SqlQuote($timestamp) : "NOW()"); + + # Take a snapshot of flags before any changes. + my $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} , + 'attach_id' => $target->{'attachment'}->{'id'} }); + my @old_summaries; + foreach my $flag (@$flags) { + my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; + push(@old_summaries, $summary); + } + + # Create new flags and update existing flags. + my $new_flags = FormToNewFlags($target, $data); + foreach my $flag (@$new_flags) { create($flag, $timestamp) } + modify($data, $timestamp); + + # In case the bug's product/component has changed, clear flags that are + # no longer valid. + &::SendSQL(" + SELECT flags.id + FROM flags, bugs LEFT OUTER JOIN flaginclusions i + ON (flags.type_id = i.type_id + AND (bugs.product_id = i.product_id OR i.product_id IS NULL) + AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) + WHERE flags.type_id = $target->{'bug'}->{'id'} + AND flags.bug_id = bugs.bug_id + AND i.type_id IS NULL + "); + clear(&::FetchOneColumn()) while &::MoreSQLData(); + &::SendSQL(" + SELECT flags.id + FROM flags, bugs, flagexclusions e + WHERE flags.type_id = $target->{'bug'}->{'id'} + AND flags.bug_id = bugs.bug_id + AND flags.type_id = e.type_id + AND (bugs.product_id = e.product_id OR e.product_id IS NULL) + AND (bugs.component_id = e.component_id OR e.component_id IS NULL) + "); + clear(&::FetchOneColumn()) while &::MoreSQLData(); + + # Take a snapshot of flags after changes. + $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} , + 'attach_id' => $target->{'attachment'}->{'id'} }); + my @new_summaries; + foreach my $flag (@$flags) { + my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; + push(@new_summaries, $summary); + } + + my $old_summaries = join(", ", @old_summaries); + my $new_summaries = join(", ", @new_summaries); + my ($removed, $added) = &::DiffStrings($old_summaries, $new_summaries); + if ($removed ne $added) { + my $sql_removed = &::SqlQuote($removed); + my $sql_added = &::SqlQuote($added); + my $field_id = &::GetFieldID('flagtypes.name'); + my $attach_id = $target->{'attachment'}->{'id'} || 'NULL'; + &::SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, " . + "bug_when, fieldid, removed, added) VALUES " . + "($target->{'bug'}->{'id'}, $attach_id, $::userid, " . + "$timestamp, $field_id, $sql_removed, $sql_added)"); + } +} + + +sub create { + # Creates a flag record in the database. + + my ($flag, $timestamp) = @_; + + # Determine the ID for the flag record by retrieving the last ID used + # and incrementing it. + &::SendSQL("SELECT MAX(id) FROM flags"); + $flag->{'id'} = (&::FetchOneColumn() || 0) + 1; + + # Insert a record for the flag into the flags table. + my $attach_id = $flag->{'target'}->{'attachment'}->{'id'} || "NULL"; + my $requestee_id = $flag->{'requestee'} ? $flag->{'requestee'}->{'id'} : "NULL"; + &::SendSQL("INSERT INTO flags (id, type_id, + bug_id, attach_id, + requestee_id, setter_id, status, + creation_date, modification_date) + VALUES ($flag->{'id'}, + $flag->{'type'}->{'id'}, + $flag->{'target'}->{'bug'}->{'id'}, + $attach_id, + $requestee_id, + $flag->{'setter'}->{'id'}, + '$flag->{'status'}', + $timestamp, + $timestamp)"); + + # Send an email notifying the relevant parties about the flag creation. + if ($flag->{'requestee'} && $flag->{'requestee'}->email_prefs->{'FlagRequestee'} + || $flag->{'type'}->{'cc_list'}) { + notify($flag, "request/created-email.txt.tmpl"); + } +} + +sub migrate { + # Moves a flag from one attachment to another. Useful for migrating + # a flag from an obsolete attachment to the attachment that obsoleted it. + + my ($old_attach_id, $new_attach_id) = @_; + + # Update the record in the flags table to point to the new attachment. + &::SendSQL("UPDATE flags " . + "SET attach_id = $new_attach_id , " . + " modification_date = NOW() " . + "WHERE attach_id = $old_attach_id"); +} + +sub modify { + # Modifies flags in the database when a user changes them. + + my ($data, $timestamp) = @_; + + # Use the date/time we were given if possible (allowing calling code + # to synchronize the comment's timestamp with those of other records). + $timestamp = ($timestamp ? &::SqlQuote($timestamp) : "NOW()"); + + # Extract a list of flags from the form data. + my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data); + + # Loop over flags and update their record in the database. + my @flags; + foreach my $id (@ids) { + my $flag = get($id); + my $status = $data->{"flag-$id"}; + + # Ignore flags the user didn't change. + next if $status eq $flag->{'status'}; + + # Since the status is validated, we know it's safe, but it's still + # tainted, so we have to detaint it before using it in a query. + &::trick_taint($status); + + if ($status eq '+' || $status eq '-') { + &::SendSQL("UPDATE flags + SET setter_id = $::userid , + status = '$status' , + modification_date = $timestamp + WHERE id = $flag->{'id'}"); + + # Send an email notifying the relevant parties about the fulfillment. + if ($flag->{'setter'}->email_prefs->{'FlagRequester'} + || $flag->{'type'}->{'cc_list'}) + { + $flag->{'status'} = $status; + notify($flag, "request/fulfilled-email.txt.tmpl"); + } + } + elsif ($status eq '?') { + &::SendSQL("UPDATE flags + SET status = '$status' , + modification_date = $timestamp + WHERE id = $flag->{'id'}"); + } + # The user unset the flag, so delete it from the database. + elsif ($status eq 'X') { + clear($flag->{'id'}); + } + + push(@flags, $flag); + } + + return \@flags; +} + +sub clear { + my ($id) = @_; + + my $flag = get($id); + + &::PushGlobalSQLState(); + &::SendSQL("DELETE FROM flags WHERE id = $id"); + &::PopGlobalSQLState(); + + # Set the flag's status to "cleared" so the email template + # knows why email is being sent about the request. + $flag->{'status'} = "X"; + + notify($flag, "request/fulfilled-email.txt.tmpl") if $flag->{'requestee'}; +} + + +################################################################################ +# Utility Functions +################################################################################ + +sub FormToNewFlags { + my ($target, $data) = @_; + + # Flag for whether or not we must get verification of the requestees + # (if the user did not uniquely identify them). + my $verify_requestees = 0; + + # Get information about the setter to add to each flag. + # Uses a conditional to suppress Perl's "used only once" warnings. + my $setter = new Bugzilla::User($::userid); + + # Extract a list of flag type IDs from field names. + my @type_ids = map(/^flag_type-(\d+)$/ ? $1 : (), keys %$data); + @type_ids = grep($data->{"flag_type-$_"} ne 'X', @type_ids); + + # Process the form data and create an array of flag objects. + my @flags; + foreach my $type_id (@type_ids) { + my $status = $data->{"flag_type-$type_id"}; + &::trick_taint($status); + + # Create the flag record and populate it with data from the form. + my $flag = { + type => Bugzilla::FlagType::get($type_id) , + target => $target , + setter => $setter , + status => $status + }; + + my $requestee_str = $data->{"requestee-$type_id"} || $data->{'requestee'}; + if ($requestee_str) { + $flag->{'requestee_str'} = $requestee_str; + MatchRequestees($flag); + $verify_requestees = 1 if scalar(@{$flag->{'requestees'}}) != 1; + } + + # Add the flag to the array of flags. + push(@flags, $flag); + } + + if ($verify_requestees) { + $vars->{'target'} = $target; + $vars->{'flags'} = \@flags; + $vars->{'form'} = $data; + $vars->{'mform'} = \%::MFORM || \%::MFORM; + + print "Content-Type: text/html\n\n" unless $vars->{'header_done'}; + $::template->process("request/verify.html.tmpl", $vars) + || &::ThrowTemplateError($template->error()); + exit; + } + + # Return the list of flags. + return \@flags; +} + +sub MatchRequestees { + my ($flag) = @_; + + my $requestee_str = $flag->{'requestee_str'}; + + # To reduce the size of queries, require the user to enter at least + # three characters of each requestee's name unless this installation + # automatically appends an email suffix to each user's login name, + # in which case we can't guarantee their names are at least three + # characters long. + if (!&Param('emailsuffix') && length($requestee_str) < 3) { + &::ThrowUserError("requestee_too_short"); + } + + # Get a list of potential requestees whose email address or real name + # matches the substring entered by the user. Try an exact match first, + # then fall back to a substring search. Limit search to 100 matches, + # since at that point there are too many to make the user wade through, + # and we need to get the user to enter a more constrictive match string. + my $user_id = &::DBname_to_id($requestee_str); + if ($user_id) { $flag->{'requestees'} = [ new Bugzilla::User($user_id) ] } + else { $flag->{'requestees'} = Bugzilla::User::match($requestee_str, 101, 1) } + + # If there is only one requestee match, make them the requestee. + if (scalar(@{$flag->{'requestees'}}) == 1) { + $flag->{'requestee'} = $flag->{'requestees'}[0]; + } + + # If there are too many requestee matches, throw an error. + elsif (scalar(@{$flag->{'requestees'}}) == 101) { + &::ThrowUserError("requestee_too_many_matches", + { requestee => $requestee_str }); + } +} + + +# Ideally, we'd use Bug.pm, but it's way too heavyweight, and it can't be +# made lighter without totally rewriting it, so we'll use this function +# until that one gets rewritten. +sub GetBug { + # Returns a hash of information about a target bug. + my ($id) = @_; + + # Save the currently running query (if any) so we do not overwrite it. + &::PushGlobalSQLState(); + + &::SendSQL("SELECT 1, short_desc, product_id, component_id + FROM bugs + WHERE bug_id = $id"); + + my $bug = { 'id' => $id }; + + ($bug->{'exists'}, $bug->{'summary'}, $bug->{'product_id'}, + $bug->{'component_id'}) = &::FetchSQLData(); + + # Restore the previously running query (if any). + &::PopGlobalSQLState(); + + return $bug; +} + +sub GetTarget { + my ($bug_id, $attach_id) = @_; + + # Create an object representing the target bug/attachment. + my $target = { 'exists' => 0 }; + + if ($attach_id) { + $target->{'attachment'} = new Attachment($attach_id); + if ($bug_id) { + # Make sure the bug and attachment IDs correspond to each other + # (i.e. this is the bug to which this attachment is attached). + $bug_id == $target->{'attachment'}->{'bug_id'} + || return { 'exists' => 0 }; + } + $target->{'bug'} = GetBug($target->{'attachment'}->{'bug_id'}); + $target->{'exists'} = $target->{'attachment'}->{'exists'}; + $target->{'type'} = "attachment"; + } + elsif ($bug_id) { + $target->{'bug'} = GetBug($bug_id); + $target->{'exists'} = $target->{'bug'}->{'exists'}; + $target->{'type'} = "bug"; + } + + return $target; +} + +sub notify { + # Sends an email notification about a flag being created or fulfilled. + + my ($flag, $template_file) = @_; + + # Work around the intricacies of globals.pl not being templatized + # by defining local variables for the $::template and $::vars globals. + my $template = $::template; + my $vars = $::vars; + + $vars->{'flag'} = $flag; + + my $message; + my $rv = + $template->process($template_file, $vars, \$message); + if (!$rv) { + print "Content-Type: text/html\n\n" unless $vars->{'header_done'}; + &::ThrowTemplateError($template->error()); + } + + my $delivery_mode = &::Param("sendmailnow") ? "" : "-ODeliveryMode=deferred"; + open(SENDMAIL, "|/usr/lib/sendmail $delivery_mode -t -i") + || die "Can't open sendmail"; + print SENDMAIL $message; + close(SENDMAIL); +} + +################################################################################ +# Private Functions +################################################################################ + +sub sqlify_criteria { + # Converts a hash of criteria into a list of SQL criteria. + + # a reference to a hash containing the criteria (field => value) + my ($criteria) = @_; + + # the generated list of SQL criteria; "1=1" is a clever way of making sure + # there's something in the list so calling code doesn't have to check list + # size before building a WHERE clause out of it + my @criteria = ("1=1"); + + # If the caller specified only bug or attachment flags, + # limit the query to those kinds of flags. + if (defined($criteria->{'target_type'})) { + if ($criteria->{'target_type'} eq 'bug') { push(@criteria, "attach_id IS NULL") } + elsif ($criteria->{'target_type'} eq 'attachment') { push(@criteria, "attach_id IS NOT NULL") } + } + + # Go through each criterion from the calling code and add it to the query. + foreach my $field (keys %$criteria) { + my $value = $criteria->{$field}; + next unless defined($value); + if ($field eq 'type_id') { push(@criteria, "type_id = $value") } + elsif ($field eq 'bug_id') { push(@criteria, "bug_id = $value") } + elsif ($field eq 'attach_id') { push(@criteria, "attach_id = $value") } + elsif ($field eq 'requestee_id') { push(@criteria, "requestee_id = $value") } + elsif ($field eq 'setter_id') { push(@criteria, "setter_id = $value") } + elsif ($field eq 'status') { push(@criteria, "status = '$value'") } + } + + return @criteria; +} + +sub perlify_record { + # Converts a row from the database into a Perl record. + my ($exists, $id, $type_id, $bug_id, $attach_id, + $requestee_id, $setter_id, $status) = @_; + + my $flag = + { + exists => $exists , + id => $id , + type => Bugzilla::FlagType::get($type_id) , + target => GetTarget($bug_id, $attach_id) , + requestee => new Bugzilla::User($requestee_id) , + setter => new Bugzilla::User($setter_id) , + status => $status , + }; + + return $flag; +} + +1; diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm new file mode 100644 index 000000000..2e272f67c --- /dev/null +++ b/Bugzilla/FlagType.pm @@ -0,0 +1,325 @@ +# -*- 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): Myk Melez + +################################################################################ +# Module Initialization +################################################################################ + +# Make it harder for us to do dangerous things in Perl. +use strict; + +# This module implements flag types for the flag tracker. +package Bugzilla::FlagType; + +# Use Bugzilla's User module which contains utilities for handling users. +use Bugzilla::User; + +# Note! This module requires that its caller have said "require CGI.pl" +# to import relevant functions from that script and its companion globals.pl. + +################################################################################ +# Global Variables +################################################################################ + +# basic sets of columns and tables for getting flag types from the database + +my @base_columns = + ("1", "flagtypes.id", "flagtypes.name", "flagtypes.description", + "flagtypes.cc_list", "flagtypes.target_type", "flagtypes.sortkey", + "flagtypes.is_active", "flagtypes.is_requestable", + "flagtypes.is_requesteeble", "flagtypes.is_multiplicable"); + +# Note: when adding tables to @base_tables, make sure to include the separator +# (i.e. a comma or words like "LEFT OUTER JOIN") before the table name, +# since tables take multiple separators based on the join type, and therefore +# it is not possible to join them later using a single known separator. + +my @base_tables = ("flagtypes"); + +################################################################################ +# Public Functions +################################################################################ + +sub get { + # Returns a hash of information about a flag type. + + my ($id) = @_; + + my $select_clause = "SELECT " . join(", ", @base_columns); + my $from_clause = "FROM " . join(" ", @base_tables); + + &::PushGlobalSQLState(); + &::SendSQL("$select_clause $from_clause WHERE flagtypes.id = $id"); + my @data = &::FetchSQLData(); + my $type = perlify_record(@data); + &::PopGlobalSQLState(); + + return $type; +} + +sub get_inclusions { + my ($id) = @_; + return get_clusions($id, "in"); +} + +sub get_exclusions { + my ($id) = @_; + return get_clusions($id, "ex"); +} + +sub get_clusions { + my ($id, $type) = @_; + + &::PushGlobalSQLState(); + &::SendSQL("SELECT products.name, components.name " . + "FROM flagtypes, flag${type}clusions " . + "LEFT OUTER JOIN products ON flag${type}clusions.product_id = products.id " . + "LEFT OUTER JOIN components ON flag${type}clusions.component_id = components.id " . + "WHERE flagtypes.id = $id AND flag${type}clusions.type_id = flagtypes.id"); + my @clusions = (); + while (&::MoreSQLData()) { + my ($product, $component) = &::FetchSQLData(); + $product ||= "Any"; + $component ||= "Any"; + push(@clusions, "$product:$component"); + } + &::PopGlobalSQLState(); + + return \@clusions; +} + +sub match { + # Queries the database for flag types matching the given criteria + # and returns the set of matching types. + + my ($criteria, $include_count) = @_; + + my @tables = @base_tables; + my @columns = @base_columns; + my $having = ""; + + # Include a count of the number of flags per type if requested. + if ($include_count) { + push(@columns, "COUNT(flags.id)"); + push(@tables, "LEFT OUTER JOIN flags ON flagtypes.id = flags.type_id"); + } + + # Generate the SQL WHERE criteria. + my @criteria = sqlify_criteria($criteria, \@tables, \@columns, \$having); + + # Build the query, grouping the types if we are counting flags. + my $select_clause = "SELECT " . join(", ", @columns); + my $from_clause = "FROM " . join(" ", @tables); + my $where_clause = "WHERE " . join(" AND ", @criteria); + + my $query = "$select_clause $from_clause $where_clause"; + $query .= " GROUP BY flagtypes.id " if ($include_count || $having ne ""); + $query .= " HAVING $having " if $having ne ""; + $query .= " ORDER BY flagtypes.sortkey, flagtypes.name"; + + # Execute the query and retrieve the results. + &::PushGlobalSQLState(); + &::SendSQL($query); + my @types; + while (&::MoreSQLData()) { + my @data = &::FetchSQLData(); + my $type = perlify_record(@data); + push(@types, $type); + } + &::PopGlobalSQLState(); + + return \@types; +} + +sub count { + # Returns the total number of flag types matching the given criteria. + + my ($criteria) = @_; + + # Generate query components. + my @tables = @base_tables; + my @columns = ("COUNT(flagtypes.id)"); + my $having = ""; + my @criteria = sqlify_criteria($criteria, \@tables, \@columns, \$having); + + # Build the query. + my $select_clause = "SELECT " . join(", ", @columns); + my $from_clause = "FROM " . join(" ", @tables); + my $where_clause = "WHERE " . join(" AND ", @criteria); + my $query = "$select_clause $from_clause $where_clause"; + $query .= " GROUP BY flagtypes.id HAVING $having " if $having ne ""; + + # Execute the query and get the results. + &::PushGlobalSQLState(); + &::SendSQL($query); + my $count = &::FetchOneColumn(); + &::PopGlobalSQLState(); + + return $count; +} + +sub validate { + my ($data) = @_; + + # Get a list of flags types to validate. Uses the "map" function + # to extract flag type IDs from form field names by matching columns + # whose name looks like "flag_type-nnn", where "nnn" is the ID, + # and returning just the ID portion of matching field names. + my @ids = map(/^flag_type-(\d+)$/ ? $1 : (), keys %$data); + + foreach my $id (@ids) + { + my $status = $data->{"flag_type-$id"}; + + # Don't bother validating types the user didn't touch. + next if $status eq "X"; + + # Make sure the flag exists. + get($id) + || &::ThrowCodeError("flag_type_nonexistent", { id => $id }); + + # Make sure the value of the field is a valid status. + grep($status eq $_, qw(X + - ?)) + || &::ThrowCodeError("flag_status_invalid", + { id => $id , status => $status }); + } +} + +sub normalize { + # Given a list of flag types, checks its flags to make sure they should + # still exist after a change to the inclusions/exclusions lists. + + # A list of IDs of flag types to normalize. + my (@ids) = @_; + + my $ids = join(", ", @ids); + + # Check for flags whose product/component is no longer included. + &::SendSQL(" + SELECT flags.id + FROM flags, bugs LEFT OUTER JOIN flaginclusions AS i + ON (flags.type_id = i.type_id + AND (bugs.product_id = i.product_id OR i.product_id IS NULL) + AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) + WHERE flags.type_id IN ($ids) + AND flags.bug_id = bugs.bug_id + AND i.type_id IS NULL + "); + Bugzilla::Flag::clear(&::FetchOneColumn()) while &::MoreSQLData(); + + &::SendSQL(" + SELECT flags.id + FROM flags, bugs, flagexclusions AS e + WHERE flags.type_id IN ($ids) + AND flags.bug_id = bugs.bug_id + AND flags.type_id = e.type_id + AND (bugs.product_id = e.product_id OR e.product_id IS NULL) + AND (bugs.component_id = e.component_id OR e.component_id IS NULL) + "); + Bugzilla::Flag::clear(&::FetchOneColumn()) while &::MoreSQLData(); +} + +################################################################################ +# Private Functions +################################################################################ + +sub sqlify_criteria { + # Converts a hash of criteria into a list of SQL criteria. + # $criteria is a reference to the criteria (field => value), + # $tables is a reference to an array of tables being accessed + # by the query, $columns is a reference to an array of columns + # being returned by the query, and $having is a reference to + # a criterion to put into the HAVING clause. + my ($criteria, $tables, $columns, $having) = @_; + + # the generated list of SQL criteria; "1=1" is a clever way of making sure + # there's something in the list so calling code doesn't have to check list + # size before building a WHERE clause out of it + my @criteria = ("1=1"); + + if ($criteria->{name}) { + push(@criteria, "flagtypes.name = " . &::SqlQuote($criteria->{name})); + } + if ($criteria->{target_type}) { + # The target type is stored in the database as a one-character string + # ("a" for attachment and "b" for bug), but this function takes complete + # names ("attachment" and "bug") for clarity, so we must convert them. + my $target_type = &::SqlQuote(substr($criteria->{target_type}, 0, 1)); + push(@criteria, "flagtypes.target_type = $target_type"); + } + if (exists($criteria->{is_active})) { + my $is_active = $criteria->{is_active} ? "1" : "0"; + push(@criteria, "flagtypes.is_active = $is_active"); + } + if ($criteria->{product_id} && $criteria->{'component_id'}) { + my $product_id = $criteria->{product_id}; + my $component_id = $criteria->{component_id}; + + # Add inclusions to the query, which simply involves joining the table + # by flag type ID and target product/component. + push(@$tables, ", flaginclusions"); + push(@criteria, "flagtypes.id = flaginclusions.type_id"); + push(@criteria, "(flaginclusions.product_id = $product_id " . + " OR flaginclusions.product_id IS NULL)"); + push(@criteria, "(flaginclusions.component_id = $component_id " . + " OR flaginclusions.component_id IS NULL)"); + + # Add exclusions to the query, which is more complicated. First of all, + # we do a LEFT JOIN so we don't miss flag types with no exclusions. + # Then, as with inclusions, we join on flag type ID and target product/ + # component. However, since we want flag types that *aren't* on the + # exclusions list, we count the number of exclusions records returned + # and use a HAVING clause to weed out types with one or more exclusions. + my $join_clause = "flagtypes.id = flagexclusions.type_id " . + "AND (flagexclusions.product_id = $product_id " . + "OR flagexclusions.product_id IS NULL) " . + "AND (flagexclusions.component_id = $component_id " . + "OR flagexclusions.component_id IS NULL)"; + push(@$tables, "LEFT JOIN flagexclusions ON ($join_clause)"); + push(@$columns, "COUNT(flagexclusions.type_id) AS num_exclusions"); + $$having = "num_exclusions = 0"; + } + + return @criteria; +} + +sub perlify_record { + # Converts data retrieved from the database into a Perl record. + + my $type = {}; + + $type->{'exists'} = $_[0]; + $type->{'id'} = $_[1]; + $type->{'name'} = $_[2]; + $type->{'description'} = $_[3]; + $type->{'cc_list'} = $_[4]; + $type->{'target_type'} = $_[5] eq "b" ? "bug" : "attachment"; + $type->{'sortkey'} = $_[6]; + $type->{'is_active'} = $_[7]; + $type->{'is_requestable'} = $_[8]; + $type->{'is_requesteeble'} = $_[9]; + $type->{'is_multiplicable'} = $_[10]; + $type->{'flag_count'} = $_[11]; + + return $type; +} + +1; diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 642965eb2..6d11c0739 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -62,6 +62,7 @@ sub init { my @fields; my @supptables; my @wherepart; + my @having = ("(cntuseringroups = cntbugingroups OR canseeanyway)"); @fields = @$fieldsref if $fieldsref; my %F; my %M; @@ -265,8 +266,8 @@ sub init { } my $chartid; - # $statusid is used by the code that queries for attachment statuses. - my $statusid = 0; + # $type_id is used by the code that queries for attachment flags. + my $type_id = 0; my $f; my $ff; my $t; @@ -358,69 +359,61 @@ sub init { } $f = "$table.$field"; }, - "^attachstatusdefs.name," => sub { - # The below has Fun with the names for attachment statuses. This - # isn't needed for changed* queries, so exclude those - the - # generic stuff will cope + "^flagtypes.name," => sub { + # Matches bugs by flag name/status. + # Note that--for the purposes of querying--a flag comprises + # its name plus its status (i.e. a flag named "review" + # with a status of "+" can be found by searching for "review+"). + + # Don't do anything if this condition is about changes to flags, + # as the generic change condition processors can handle those. return if ($t =~ m/^changed/); - - # Searching for "status != 'bar'" wants us to look for an - # attachment without the 'bar' status, not for an attachment with - # a status not equal to 'bar' (Which would pick up an attachment - # with more than one status). We do this by LEFT JOINS, after - # grabbing the matching attachment status ids. - # Note that this still won't find bugs with no attachments, since - # that isn't really what people would expect. - - # First, get the attachment status ids, using the other funcs - # to match the WHERE term. - # Note that we need to reverse the negated bits for this to work - # This somewhat abuses the definitions of the various terms - - # eg, does 'contains all' mean that the status has to contain all - # those words, or that all those words must be exact matches to - # statuses, which must all be on a single attachment, or should - # the match on the status descriptions be a contains match, too? - - my $inverted = 0; - if ($t =~ m/not(.*)/) { - $t = $1; - $inverted = 1; - } - - $ref = $funcsbykey{",$t"}; - &$ref; - &::SendSQL("SELECT id FROM attachstatusdefs WHERE $term"); - - my @as_ids; - while (&::MoreSQLData()) { - push @as_ids, &::FetchOneColumn(); - } - - # When searching for multiple statuses within a single boolean chart, - # we want to match each status record separately. In other words, - # "status = 'foo' AND status = 'bar'" should match attachments with - # one status record equal to "foo" and another one equal to "bar", - # not attachments where the same status record equals both "foo" and - # "bar" (which is nonsensical). In order to do this we must add an - # additional counter to the end of the "attachstatuses" table - # reference. - ++$statusid; - - my $attachtable = "attachments_$chartid"; - my $statustable = "attachstatuses_${chartid}_$statusid"; - - push(@supptables, "attachments $attachtable"); - my $join = "LEFT JOIN attachstatuses $statustable ON ". - "($attachtable.attach_id = $statustable.attach_id AND " . - "$statustable.statusid IN (" . join(",", @as_ids) . "))"; - push(@supptables, $join); - push(@wherepart, "bugs.bug_id = $attachtable.bug_id"); - if ($inverted) { - $term = "$statustable.statusid IS NULL"; - } else { - $term = "$statustable.statusid IS NOT NULL"; + + # Add the flags and flagtypes tables to the query. We do + # a left join here so bugs without any flags still match + # negative conditions (f.e. "flag isn't review+"). + my $flags = "flags_$chartid"; + push(@supptables, "LEFT JOIN flags $flags " . + "ON bugs.bug_id = $flags.bug_id"); + my $flagtypes = "flagtypes_$chartid"; + push(@supptables, "LEFT JOIN flagtypes $flagtypes " . + "ON $flags.type_id = $flagtypes.id"); + + # Generate the condition by running the operator-specific function. + # Afterwards the condition resides in the global $term variable. + $ff = "CONCAT($flagtypes.name, $flags.status)"; + &{$funcsbykey{",$t"}}; + + # If this is a negative condition (f.e. flag isn't "review+"), + # we only want bugs where all flags match the condition, not + # those where any flag matches, which needs special magic. + # Instead of adding the condition to the WHERE clause, we select + # the number of flags matching the condition and the total number + # of flags on each bug, then compare them in a HAVING clause. + # If the numbers are the same, all flags match the condition, + # so this bug should be included. + if ($t =~ m/not/) { + push(@fields, "SUM($ff IS NOT NULL) AS allflags_$chartid"); + push(@fields, "SUM($term) AS matchingflags_$chartid"); + push(@having, "allflags_$chartid = matchingflags_$chartid"); + $term = "0=0"; } }, + "^requesters.login_name," => sub { + push(@supptables, "flags flags_$chartid"); + push(@wherepart, "bugs.bug_id = flags_$chartid.bug_id"); + push(@supptables, "profiles requesters_$chartid"); + push(@wherepart, "flags_$chartid.requester_id = requesters_$chartid.userid"); + $f = "requesters_$chartid.login_name"; + }, + "^setters.login_name," => sub { + push(@supptables, "flags flags_$chartid"); + push(@wherepart, "bugs.bug_id = flags_$chartid.bug_id"); + push(@supptables, "profiles setters_$chartid"); + push(@wherepart, "flags_$chartid.setter_id = setters_$chartid.userid"); + $f = "setters_$chartid.login_name"; + }, + "^changedin," => sub { $f = "(to_days(now()) - to_days(bugs.delta_ts))"; }, @@ -817,8 +810,7 @@ sub init { # Make sure we create a legal SQL query. @andlist = ("1 = 1") if !@andlist; - my $query = ("SELECT DISTINCT " . - join(', ', @fields) . + my $query = ("SELECT " . join(', ', @fields) . ", COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, " . " COUNT(DISTINCT bgmap.group_id) AS cntbugingroups, " . " ((COUNT(DISTINCT ccmap.who) AND cclist_accessible) " . @@ -834,11 +826,9 @@ sub init { " LEFT JOIN cc AS ccmap " . " ON ccmap.who = $::userid AND ccmap.bug_id = bugs.bug_id " . " WHERE " . join(' AND ', (@wherepart, @andlist)) . - " GROUP BY bugs.bug_id " . - " HAVING cntuseringroups = cntbugingroups" . - " OR canseeanyway" - ); - + " GROUP BY bugs.bug_id" . + " HAVING " . join(" AND ", @having)); + if ($debug) { print "

" . value_quote($query) . "

\n"; exit; diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm new file mode 100644 index 000000000..72870d544 --- /dev/null +++ b/Bugzilla/User.pm @@ -0,0 +1,176 @@ +# -*- 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): Myk Melez + +################################################################################ +# Module Initialization +################################################################################ + +# Make it harder for us to do dangerous things in Perl. +use strict; + +# This module implements utilities for dealing with Bugzilla users. +package Bugzilla::User; + +################################################################################ +# Functions +################################################################################ + +my $user_cache = {}; +sub new { + # Returns a hash of information about a particular user. + + my $invocant = shift; + my $class = ref($invocant) || $invocant; + + my $exists = 1; + my ($id, $name, $email) = @_; + + return undef if !$id; + return $user_cache->{$id} if exists($user_cache->{$id}); + + my $self = { 'id' => $id }; + + bless($self, $class); + + if (!$name && !$email) { + &::PushGlobalSQLState(); + &::SendSQL("SELECT 1, realname, login_name FROM profiles WHERE userid = $id"); + ($exists, $name, $email) = &::FetchSQLData(); + &::PopGlobalSQLState(); + } + + $self->{'name'} = $name; + $self->{'email'} = $email; + $self->{'exists'} = $exists; + + # Generate a string to identify the user by name + email if the user + # has a name or by email only if she doesn't. + $self->{'identity'} = $name ? "$name <$email>" : $email; + + # Generate a user "nickname" -- i.e. a shorter, not-necessarily-unique name + # by which to identify the user. Currently the part of the user's email + # address before the at sign (@), but that could change, especially if we + # implement usernames not dependent on email address. + my @email_components = split("@", $email); + $self->{'nick'} = $email_components[0]; + + $user_cache->{$id} = $self; + + return $self; +} + +sub match { + # Generates a list of users whose login name (email address) or real name + # matches a substring. + + # $str contains the string to match against, while $limit contains the + # maximum number of records to retrieve. + my ($str, $limit, $exclude_disabled) = @_; + + # Build the query. + my $sqlstr = &::SqlQuote($str); + my $qry = " + SELECT userid, realname, login_name + FROM profiles + WHERE (INSTR(login_name, $sqlstr) OR INSTR(realname, $sqlstr)) + "; + $qry .= "AND disabledtext = '' " if $exclude_disabled; + $qry .= "ORDER BY realname, login_name "; + $qry .= "LIMIT $limit " if $limit; + + # Execute the query, retrieve the results, and make them into User objects. + my @users; + &::PushGlobalSQLState(); + &::SendSQL($qry); + push(@users, new Bugzilla::User(&::FetchSQLData())) while &::MoreSQLData(); + &::PopGlobalSQLState(); + + return \@users; +} + +sub email_prefs { + # Get or set (not implemented) the user's email notification preferences. + + my $self = shift; + + # If the calling code is setting the email preferences, update the object + # but don't do anything else. This needs to write email preferences back + # to the database. + if (@_) { $self->{email_prefs} = shift; return; } + + # If we already got them from the database, return the existing values. + return $self->{email_prefs} if $self->{email_prefs}; + + # Retrieve the values from the database. + &::SendSQL("SELECT emailflags FROM profiles WHERE userid = $self->{id}"); + my ($flags) = &::FetchSQLData(); + + my @roles = qw(Owner Reporter QAcontact CClist Voter); + my @reasons = qw(Removeme Comments Attachments Status Resolved Keywords + CC Other Unconfirmed); + + # If the prefs are empty, this user hasn't visited the email pane + # of userprefs.cgi since before the change to use the "emailflags" + # column, so initialize that field with the default prefs. + if (!$flags) { + # Create a default prefs string that causes the user to get all email. + $flags = "ExcludeSelf~on~FlagRequestee~on~FlagRequester~on~"; + foreach my $role (@roles) { + foreach my $reason (@reasons) { + $flags .= "email$role$reason~on~"; + } + } + chop $flags; + } + + # Convert the prefs from the flags string from the database into + # a Perl record. The 255 param is here because split will trim + # any trailing null fields without a third param, which causes Perl + # to eject lots of warnings. Any suitably large number would do. + my $prefs = { split(/~/, $flags, 255) }; + + # Determine the value of the "excludeself" global email preference. + # Note that the value of "excludeself" is assumed to be off if the + # preference does not exist in the user's list, unlike other + # preferences whose value is assumed to be on if they do not exist. + $prefs->{ExcludeSelf} = + exists($prefs->{ExcludeSelf}) && $prefs->{ExcludeSelf} eq "on"; + + # Determine the value of the global request preferences. + foreach my $pref qw(FlagRequestee FlagRequester) { + $prefs->{$pref} = !exists($prefs->{$pref}) || $prefs->{$pref} eq "on"; + } + + # Determine the value of the rest of the preferences by looping over + # all roles and reasons and converting their values to Perl booleans. + foreach my $role (@roles) { + foreach my $reason (@reasons) { + my $key = "email$role$reason"; + $prefs->{$key} = !exists($prefs->{$key}) || $prefs->{$key} eq "on"; + } + } + + $self->{email_prefs} = $prefs; + + return $self->{email_prefs}; +} + +1; diff --git a/attachment.cgi b/attachment.cgi index 4d5fea475..b185312c6 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -46,6 +46,10 @@ if ($^O eq 'MSWin32') { # Include the Bugzilla CGI and general utility library. require "CGI.pl"; +# Use these modules to handle flags. +use Bugzilla::Flag; +use Bugzilla::FlagType; + # Establish a connection to the database backend. ConnectToDatabase(); @@ -110,7 +114,8 @@ elsif ($action eq "update") validateContentType() unless $::FORM{'ispatch'}; validateIsObsolete(); validatePrivate(); - validateStatuses(); + Bugzilla::Flag::validate(\%::FORM); + Bugzilla::FlagType::validate(\%::FORM); update(); } else @@ -240,29 +245,6 @@ sub validatePrivate $::FORM{'isprivate'} = $::FORM{'isprivate'} ? 1 : 0; } -sub validateStatuses -{ - # Get a list of attachment statuses that are valid for this attachment. - PushGlobalSQLState(); - SendSQL("SELECT attachstatusdefs.id - FROM attachments, bugs, attachstatusdefs - WHERE attachments.attach_id = $::FORM{'id'} - AND attachments.bug_id = bugs.bug_id - AND attachstatusdefs.product_id = bugs.product_id"); - my @statusdefs; - push(@statusdefs, FetchSQLData()) while MoreSQLData(); - PopGlobalSQLState(); - - foreach my $status (@{$::MFORM{'status'}}) - { - grep($_ == $status, @statusdefs) - || ThrowUserError("invalid_attach_status"); - - # We have tested that the status is valid, so it can be detainted - detaint_natural($status); - } -} - sub validateData { $::FORM{'data'} @@ -380,18 +362,6 @@ sub viewall # !!! Yuck, what an ugly hack. Fix it! $a{'isviewable'} = ( $a{'contenttype'} =~ /^(text|image|application\/vnd\.mozilla\.)/ ); - # Retrieve a list of status flags that have been set on the attachment. - PushGlobalSQLState(); - SendSQL("SELECT name - FROM attachstatuses, attachstatusdefs - WHERE attach_id = $a{'attachid'} - AND attachstatuses.statusid = attachstatusdefs.id - ORDER BY sortkey"); - my @statuses; - push(@statuses, FetchSQLData()) while MoreSQLData(); - $a{'statuses'} = \@statuses; - PopGlobalSQLState(); - # Add the hash representing the attachment to the array of attachments. push @attachments, \%a; } @@ -491,10 +461,14 @@ sub insert # Make existing attachments obsolete. my $fieldid = GetFieldID('attachments.isobsolete'); - foreach my $attachid (@{$::MFORM{'obsolete'}}) { - SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $attachid"); + foreach my $obsolete_id (@{$::MFORM{'obsolete'}}) { + SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $obsolete_id"); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($::FORM{'bugid'}, $attachid, $::userid, NOW(), $fieldid, '0', '1')"); + VALUES ($::FORM{'bugid'}, $obsolete_id, $::userid, NOW(), $fieldid, '0', '1')"); + # If the obsolete attachment has pending flags, migrate them to the new attachment. + if (Bugzilla::Flag::count({ 'attach_id' => $obsolete_id , 'status' => 'pending' })) { + Bugzilla::Flag::migrate($obsolete_id, $attachid); + } } # Send mail to let people know the attachment has been created. Uses a @@ -544,32 +518,6 @@ sub edit # !!! Yuck, what an ugly hack. Fix it! my $isviewable = ( $contenttype =~ /^(text|image|application\/vnd\.mozilla\.)/ ); - # Retrieve a list of status flags that have been set on the attachment. - my %statuses; - SendSQL("SELECT id, name - FROM attachstatuses JOIN attachstatusdefs - WHERE attachstatuses.statusid = attachstatusdefs.id - AND attach_id = $::FORM{'id'}"); - while ( my ($id, $name) = FetchSQLData() ) - { - $statuses{$id} = $name; - } - - # Retrieve a list of statuses for this bug's product, and build an array - # of hashes in which each hash is a status flag record. - # ???: Move this into versioncache or its own routine? - my @statusdefs; - SendSQL("SELECT id, name - FROM attachstatusdefs, bugs - WHERE bug_id = $bugid - AND attachstatusdefs.product_id = bugs.product_id - ORDER BY sortkey"); - while ( MoreSQLData() ) - { - my ($id, $name) = FetchSQLData(); - push @statusdefs, { 'id' => $id , 'name' => $name }; - } - # Retrieve a list of attachments for this bug as well as a summary of the bug # to use in a navigation bar across the top of the screen. SendSQL("SELECT attach_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id"); @@ -577,7 +525,20 @@ sub edit push(@bugattachments, FetchSQLData()) while (MoreSQLData()); SendSQL("SELECT short_desc FROM bugs WHERE bug_id = $bugid"); my ($bugsummary) = FetchSQLData(); - + + # Get a list of flag types that can be set for this attachment. + SendSQL("SELECT product_id, component_id FROM bugs WHERE bug_id = $bugid"); + my ($product_id, $component_id) = FetchSQLData(); + my $flag_types = Bugzilla::FlagType::match({ 'target_type' => 'attachment' , + 'product_id' => $product_id , + 'component_id' => $component_id , + 'is_active' => 1}); + foreach my $flag_type (@$flag_types) { + $flag_type->{'flags'} = Bugzilla::Flag::match({ 'type_id' => $flag_type->{'id'}, + 'attach_id' => $::FORM{'id'} }); + } + $vars->{'flag_types'} = $flag_types; + # Define the variables and functions that will be passed to the UI template. $vars->{'attachid'} = $::FORM{'id'}; $vars->{'description'} = $description; @@ -589,8 +550,6 @@ sub edit $vars->{'isobsolete'} = $isobsolete; $vars->{'isprivate'} = $isprivate; $vars->{'isviewable'} = $isviewable; - $vars->{'statuses'} = \%statuses; - $vars->{'statusdefs'} = \@statusdefs; $vars->{'attachments'} = \@bugattachments; # Return the appropriate HTTP response headers. @@ -604,7 +563,7 @@ sub edit sub update { - # Update an attachment record. + # Updates an attachment record. # Get the bug ID for the bug to which this attachment is attached. SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); @@ -616,8 +575,11 @@ sub update } # Lock database tables in preparation for updating the attachment. - SendSQL("LOCK TABLES attachments WRITE , attachstatuses WRITE , - attachstatusdefs READ , fielddefs READ , bugs_activity WRITE"); + SendSQL("LOCK TABLES attachments WRITE , flags WRITE , " . + "flagtypes READ , fielddefs READ , bugs_activity WRITE, " . + "flaginclusions AS i READ, flagexclusions AS e READ, " . + "bugs READ, profiles READ"); + # Get a copy of the attachment record before we make changes # so we can record those changes in the activity table. SendSQL("SELECT description, mimetype, filename, ispatch, isobsolete, isprivate @@ -625,41 +587,6 @@ sub update my ($olddescription, $oldcontenttype, $oldfilename, $oldispatch, $oldisobsolete, $oldisprivate) = FetchSQLData(); - # Get the list of old status flags. - SendSQL("SELECT attachstatusdefs.name - FROM attachments, attachstatuses, attachstatusdefs - WHERE attachments.attach_id = $::FORM{'id'} - AND attachments.attach_id = attachstatuses.attach_id - AND attachstatuses.statusid = attachstatusdefs.id - ORDER BY attachstatusdefs.sortkey - "); - my @oldstatuses; - while (MoreSQLData()) { - push(@oldstatuses, FetchSQLData()); - } - my $oldstatuslist = join(', ', @oldstatuses); - - # Update the database with the new status flags. - SendSQL("DELETE FROM attachstatuses WHERE attach_id = $::FORM{'id'}"); - foreach my $statusid (@{$::MFORM{'status'}}) - { - SendSQL("INSERT INTO attachstatuses (attach_id, statusid) VALUES ($::FORM{'id'}, $statusid)"); - } - - # Get the list of new status flags. - SendSQL("SELECT attachstatusdefs.name - FROM attachments, attachstatuses, attachstatusdefs - WHERE attachments.attach_id = $::FORM{'id'} - AND attachments.attach_id = attachstatuses.attach_id - AND attachstatuses.statusid = attachstatusdefs.id - ORDER BY attachstatusdefs.sortkey - "); - my @newstatuses; - while (MoreSQLData()) { - push(@newstatuses, FetchSQLData()); - } - my $newstatuslist = join(', ', @newstatuses); - # Quote the description and content type for use in the SQL UPDATE statement. my $quoteddescription = SqlQuote($::FORM{'description'}); my $quotedcontenttype = SqlQuote($::FORM{'contenttype'}); @@ -677,18 +604,23 @@ sub update WHERE attach_id = $::FORM{'id'} "); + # Figure out when the changes were made. + SendSQL("SELECT NOW()"); + my $timestamp = FetchOneColumn(); + # Record changes in the activity table. + my $sql_timestamp = SqlQuote($timestamp); if ($olddescription ne $::FORM{'description'}) { my $quotedolddescription = SqlQuote($olddescription); my $fieldid = GetFieldID('attachments.description'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedolddescription, $quoteddescription)"); + VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $quotedolddescription, $quoteddescription)"); } if ($oldcontenttype ne $::FORM{'contenttype'}) { my $quotedoldcontenttype = SqlQuote($oldcontenttype); my $fieldid = GetFieldID('attachments.mimetype'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldcontenttype, $quotedcontenttype)"); + VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $quotedoldcontenttype, $quotedcontenttype)"); } if ($oldfilename ne $::FORM{'filename'}) { my $quotedoldfilename = SqlQuote($oldfilename); @@ -699,48 +631,26 @@ sub update if ($oldispatch ne $::FORM{'ispatch'}) { my $fieldid = GetFieldID('attachments.ispatch'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldispatch, $::FORM{'ispatch'})"); + VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $oldispatch, $::FORM{'ispatch'})"); } if ($oldisobsolete ne $::FORM{'isobsolete'}) { my $fieldid = GetFieldID('attachments.isobsolete'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldisobsolete, $::FORM{'isobsolete'})"); + VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $oldisobsolete, $::FORM{'isobsolete'})"); } if ($oldisprivate ne $::FORM{'isprivate'}) { my $fieldid = GetFieldID('attachments.isprivate'); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldisprivate, $::FORM{'isprivate'})"); } - if ($oldstatuslist ne $newstatuslist) { - my ($removed, $added) = DiffStrings($oldstatuslist, $newstatuslist); - my $quotedremoved = SqlQuote($removed); - my $quotedadded = SqlQuote($added); - my $fieldid = GetFieldID('attachstatusdefs.name'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedremoved, $quotedadded)"); - } - + + # Update flags. + my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); + Bugzilla::Flag::process($target, $timestamp, \%::FORM); + # Unlock all database tables now that we are finished updating the database. SendSQL("UNLOCK TABLES"); - # If this installation has enabled the request manager, let the manager know - # an attachment was updated so it can check for requests on that attachment - # and fulfill them. The request manager allows users to request database - # changes of other users and tracks the fulfillment of those requests. When - # an attachment record is updated and the request manager is called, it will - # fulfill those requests that were requested of the user performing the update - # which are requests for the attachment being updated. - #my $requests; - #if (Param('userequestmanager')) - #{ - # use Request; - # # Specify the fieldnames that have been updated. - # my @fieldnames = ('description', 'mimetype', 'status', 'ispatch', 'isobsolete'); - # # Fulfill pending requests. - # $requests = Request::fulfillRequest('attachment', $::FORM{'id'}, @fieldnames); - # $vars->{'requests'} = $requests; - #} - # If the user submitted a comment while editing the attachment, # add the comment to the bug. if ( $::FORM{'comment'} ) @@ -772,10 +682,10 @@ sub update my $neverused = $::userid; # Append the comment to the list of comments in the database. - AppendComment($bugid, $who, $wrappedcomment, $::FORM{'isprivate'}); + AppendComment($bugid, $who, $wrappedcomment, $::FORM{'isprivate'}, $timestamp); } - + # Send mail to let people know the bug has changed. Uses a special syntax # of the "open" and "exec" commands to capture the output of "processmail", # which "system" doesn't allow, without running the command through a shell, diff --git a/bug_form.pl b/bug_form.pl index dfffca9b8..d087b4db2 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -28,6 +28,10 @@ use RelationSet; # 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. @@ -76,10 +80,10 @@ sub show_bug { # Populate the bug hash with the info we get directly from the DB. 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, + 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), delta_ts calc_disp_date @@ -101,12 +105,12 @@ sub show_bug { my $value; my $disp_date; my @row = FetchSQLData(); - 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", "calc_disp_date") + 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", "calc_disp_date") { $value = shift(@row); if ($field eq "calc_disp_date") { @@ -197,6 +201,28 @@ sub show_bug { # 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'}, + 'is_active' => 1 }); + foreach my $flag_type (@$flag_types) { + $flag_type->{'flags'} = + Bugzilla::Flag::match({ 'bug_id' => $id , + 'target_type' => 'bug' }); + } + $vars->{'flag_types'} = $flag_types; + + # The number of types of flags that can be set on attachments + # to this bug. If none, flags won't be shown in the list of attachments. + $vars->{'num_attachment_flag_types'} = + Bugzilla::FlagType::count({ 'target_type' => 'a', + 'product_id' => $bug{'product_id'}, + 'component_id' => $bug{'component_id'}, + 'is_active' => 1 }); # Dependencies my @list; diff --git a/checksetup.pl b/checksetup.pl index 27bcf26f9..737a629e8 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1336,24 +1336,65 @@ $table{attachments} = index(bug_id), index(creation_ts)'; -# 2001-05-05 myk@mozilla.org: Tables to support attachment statuses. -# "attachstatuses" stores one record for each status on each attachment. -# "attachstatusdefs" defines the statuses that can be set on attachments. - -$table{attachstatuses} = - ' - attach_id MEDIUMINT NOT NULL , - statusid SMALLINT NOT NULL , - PRIMARY KEY(attach_id, statusid) +# September 2002 myk@mozilla.org: Tables to support status flags, +# which replace attachment statuses and allow users to flag bugs +# or attachments with statuses (review+, approval-, etc.). +# +# "flags" stores one record for each flag on each bug/attachment. +# "flagtypes" defines the types of flags that can be set. +# "flaginclusions" and "flagexclusions" specify the products/components +# a bug/attachment must belong to in order for flags of a given type +# to be set for them. + +$table{flags} = + 'id MEDIUMINT NOT NULL PRIMARY KEY , + type_id SMALLINT NOT NULL , + status CHAR(1) NOT NULL , + + bug_id MEDIUMINT NOT NULL , + attach_id MEDIUMINT NULL , + + creation_date DATETIME NOT NULL , + modification_date DATETIME NULL , + + setter_id MEDIUMINT NULL , + requestee_id MEDIUMINT NULL , + + INDEX(bug_id, attach_id) , + INDEX(setter_id) , + INDEX(requestee_id) '; -$table{attachstatusdefs} = - ' - id SMALLINT NOT NULL PRIMARY KEY , - name VARCHAR(50) NOT NULL , - description MEDIUMTEXT NULL , - sortkey SMALLINT NOT NULL DEFAULT 0 , - product_id SMALLINT NOT NULL +$table{flagtypes} = + 'id SMALLINT NOT NULL PRIMARY KEY , + name VARCHAR(50) NOT NULL , + description TEXT NULL , + cc_list VARCHAR(200) NULL , + + target_type CHAR(1) NOT NULL DEFAULT \'b\' , + + is_active TINYINT NOT NULL DEFAULT 1 , + is_requestable TINYINT NOT NULL DEFAULT 0 , + is_requesteeble TINYINT NOT NULL DEFAULT 0 , + is_multiplicable TINYINT NOT NULL DEFAULT 0 , + + sortkey SMALLINT NOT NULL DEFAULT 0 + '; + +$table{flaginclusions} = + 'type_id SMALLINT NOT NULL , + product_id SMALLINT NULL , + component_id SMALLINT NULL , + + INDEX(type_id, product_id, component_id) + '; + +$table{flagexclusions} = + 'type_id SMALLINT NOT NULL , + product_id SMALLINT NULL , + component_id SMALLINT NULL , + + INDEX(type_id, product_id, component_id) '; # @@ -1792,7 +1833,7 @@ AddFDef("attachments.mimetype", "Attachment mime type", 0); AddFDef("attachments.ispatch", "Attachment is patch", 0); AddFDef("attachments.isobsolete", "Attachment is obsolete", 0); AddFDef("attachments.isprivate", "Attachment is private", 0); -AddFDef("attachstatusdefs.name", "Attachment Status", 0); + AddFDef("target_milestone", "Target Milestone", 0); AddFDef("delta_ts", "Last changed date", 0); AddFDef("(to_days(now()) - to_days(bugs.delta_ts))", "Days since bug changed", @@ -1807,6 +1848,10 @@ AddFDef("bug_group", "Group", 0); # Oops. Bug 163299 $dbh->do("DELETE FROM fielddefs WHERE name='cc_accessible'"); +AddFDef("flagtypes.name", "Flag", 0); +AddFDef("requesters.login_name", "Flag Requester", 0); +AddFDef("setters.login_name", "Flag Setter", 0); + ########################################################################### # Detect changed local settings ########################################################################### @@ -3246,6 +3291,133 @@ if (GetFieldDef("profiles", "groupset")) { $dbh->do("DELETE FROM fielddefs WHERE name = " . $dbh->quote('groupset')); } +# September 2002 myk@mozilla.org bug 98801 +# Convert the attachment statuses tables into flags tables. +if (TableExists("attachstatuses") && TableExists("attachstatusdefs")) { + print "Converting attachment statuses to flags...\n"; + + # Get IDs for the old attachment status and new flag fields. + $sth = $dbh->prepare("SELECT fieldid FROM fielddefs " . + "WHERE name='attachstatusdefs.name'"); + $sth->execute(); + my $old_field_id = $sth->fetchrow_arrayref()->[0] || 0; + + $sth = $dbh->prepare("SELECT fieldid FROM fielddefs " . + "WHERE name='flagtypes.name'"); + $sth->execute(); + my $new_field_id = $sth->fetchrow_arrayref()->[0]; + + # Convert attachment status definitions to flag types. If more than one + # status has the same name and description, it is merged into a single + # status with multiple inclusion records. + $sth = $dbh->prepare("SELECT id, name, description, sortkey, product_id " . + "FROM attachstatusdefs"); + + # status definition IDs indexed by name/description + my $def_ids = {}; + + # merged IDs and the IDs they were merged into. The key is the old ID, + # the value is the new one. This allows us to give statuses the right + # ID when we convert them over to flags. This map includes IDs that + # weren't merged (in this case the old and new IDs are the same), since + # it makes the code simpler. + my $def_id_map = {}; + + $sth->execute(); + while (my ($id, $name, $desc, $sortkey, $prod_id) = $sth->fetchrow_array()) { + my $key = $name . $desc; + if (!$def_ids->{$key}) { + $def_ids->{$key} = $id; + my $quoted_name = $dbh->quote($name); + my $quoted_desc = $dbh->quote($desc); + $dbh->do("INSERT INTO flagtypes (id, name, description, sortkey, " . + "target_type) VALUES ($id, $quoted_name, $quoted_desc, " . + "$sortkey, 'a')"); + } + $def_id_map->{$id} = $def_ids->{$key}; + $dbh->do("INSERT INTO flaginclusions (type_id, product_id) " . + "VALUES ($def_id_map->{$id}, $prod_id)"); + } + + # Note: even though we've converted status definitions, we still can't drop + # the table because we need it to convert the statuses themselves. + + # Convert attachment statuses to flags. To do this we select the statuses + # from the status table and then, for each one, figure out who set it + # and when they set it from the bugs activity table. + my $id = 0; + $sth = $dbh->prepare("SELECT attachstatuses.attach_id, attachstatusdefs.id, " . + "attachstatusdefs.name, attachments.bug_id " . + "FROM attachstatuses, attachstatusdefs, attachments " . + "WHERE attachstatuses.statusid = attachstatusdefs.id " . + "AND attachstatuses.attach_id = attachments.attach_id"); + + # a query to determine when the attachment status was set and who set it + my $sth2 = $dbh->prepare("SELECT added, who, bug_when " . + "FROM bugs_activity " . + "WHERE bug_id = ? AND attach_id = ? " . + "AND fieldid = $old_field_id " . + "ORDER BY bug_when DESC"); + + $sth->execute(); + while (my ($attach_id, $def_id, $status, $bug_id) = $sth->fetchrow_array()) { + ++$id; + + # Determine when the attachment status was set and who set it. + # We should always be able to find out this info from the bug activity, + # but we fall back to default values just in case. + $sth2->execute($bug_id, $attach_id); + my ($added, $who, $when); + while (($added, $who, $when) = $sth2->fetchrow_array()) { + last if $added =~ /(^|[, ]+)\Q$status\E([, ]+|$)/; + } + $who = $dbh->quote($who); # "NULL" by default if $who is undefined + $when = $when ? $dbh->quote($when) : "NOW()"; + + + $dbh->do("INSERT INTO flags (id, type_id, status, bug_id, attach_id, " . + "creation_date, modification_date, requestee_id, setter_id) " . + "VALUES ($id, $def_id_map->{$def_id}, '+', $bug_id, " . + "$attach_id, $when, $when, NULL, $who)"); + } + + # Now that we've converted both tables we can drop them. + $dbh->do("DROP TABLE attachstatuses"); + $dbh->do("DROP TABLE attachstatusdefs"); + + # Convert activity records for attachment statuses into records for flags. + my $sth = $dbh->prepare("SELECT attach_id, who, bug_when, added, removed " . + "FROM bugs_activity WHERE fieldid = $old_field_id"); + $sth->execute(); + while (my ($attach_id, $who, $when, $old_added, $old_removed) = + $sth->fetchrow_array()) + { + my @additions = split(/[, ]+/, $old_added); + @additions = map("$_+", @additions); + my $new_added = $dbh->quote(join(", ", @additions)); + + my @removals = split(/[, ]+/, $old_removed); + @removals = map("$_+", @removals); + my $new_removed = $dbh->quote(join(", ", @removals)); + + $old_added = $dbh->quote($old_added); + $old_removed = $dbh->quote($old_removed); + $who = $dbh->quote($who); + $when = $dbh->quote($when); + + $dbh->do("UPDATE bugs_activity SET fieldid = $new_field_id, " . + "added = $new_added, removed = $new_removed " . + "WHERE attach_id = $attach_id AND who = $who " . + "AND bug_when = $when AND fieldid = $old_field_id " . + "AND added = $old_added AND removed = $old_removed"); + } + + # Remove the attachment status field from the field definitions. + $dbh->do("DELETE FROM fielddefs WHERE name='attachstatusdefs.name'"); + + print "done.\n"; +} + # If you had to change the --TABLE-- definition in any way, then add your # differential change code *** A B O V E *** this comment. # diff --git a/editattachstatuses.cgi b/editattachstatuses.cgi deleted file mode 100755 index eedf2add4..000000000 --- a/editattachstatuses.cgi +++ /dev/null @@ -1,347 +0,0 @@ -#!/usr/bonsaitools/bin/perl -w -# -*- 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 -# Myk Melez - -################################################################################ -# Script Initialization -################################################################################ - -# Make it harder for us to do dangerous things in Perl. -use strict; -use lib "."; - -use vars qw( - $template - $vars -); - -# Include the Bugzilla CGI and general utility library. -require "CGI.pl"; - -# Establish a connection to the database backend. -ConnectToDatabase(); - -# Make sure the user is logged in and is allowed to edit products -# (i.e. the user has "editcomponents" privileges), since attachment -# statuses are product-specific. -confirm_login(); -UserInGroup("editcomponents") - || DisplayError("You are not authorized to administer attachment statuses.") - && exit; - -################################################################################ -# Main Body Execution -################################################################################ - -# All calls to this script should contain an "action" variable whose value -# determines what the user wants to do. The code below checks the value of -# that variable and runs the appropriate code. - -# Determine whether to use the action specified by the user or the default. -my $action = $::FORM{'action'} || 'list'; - -if ($action eq "list") -{ - list(); -} -elsif ($action eq "create") -{ - create(); -} -elsif ($action eq "insert") -{ - validateName(); - validateDescription(); - validateSortKey(); - validateProduct(); - insert(); -} -elsif ($action eq "edit") -{ - edit(); -} -elsif ($action eq "update") -{ - validateID(); - validateName(); - validateDescription(); - validateSortKey(); - update(); -} -elsif ($action eq "confirmdelete") -{ - validateID(); - confirmDelete(); -} -elsif ($action eq "delete") -{ - validateID(); - deleteStatus(); -} -else -{ - DisplayError("I could not figure out what you wanted to do.") -} - -exit; - -################################################################################ -# Data Validation -################################################################################ - -sub validateID -{ - $::FORM{'id'} =~ /^[1-9][0-9]*$/ - || DisplayError("The status ID is not a positive integer.") - && exit; - - SendSQL("SELECT 1 FROM attachstatusdefs WHERE id = $::FORM{'id'}"); - my ($defexists) = FetchSQLData(); - $defexists - || DisplayError("The status with ID #$::FORM{'id'} does not exist.") - && exit; -} - -sub validateName -{ - $::FORM{'name'} - || DisplayError("You must enter a name for the status.") - && exit; - - $::FORM{'name'} !~ /[\s,]/ - || DisplayError("The status name cannot contain commas or whitespace.") - && exit; - - length($::FORM{'name'}) <= 50 - || DisplayError("The status name cannot be more than 50 characters long.") - && exit; -} - -sub validateDescription -{ - $::FORM{'desc'} - || DisplayError("You must enter a description of the status.") - && exit; -} - -sub validateSortKey -{ - $::FORM{'sortkey'} =~ /^\d+$/ - && $::FORM{'sortkey'} < 32768 - || DisplayError("The sort key must be an integer between 0 and 32767 inclusive.") - && exit; -} - -sub validateProduct -{ - # Retrieve a list of products. - SendSQL("SELECT name FROM products"); - my @products; - push(@products, FetchSQLData()) while MoreSQLData(); - - grep($_ eq $::FORM{'product'}, @products) - || DisplayError("You must select an existing product for the status.") - && exit; -} - -################################################################################ -# Functions -################################################################################ - -sub list -{ - # Administer attachment status flags, which is the set of status flags - # that can be applied to an attachment. - - # If the user is seeing this screen as a result of doing something to - # an attachment status flag, display a message about what happened - # to that flag (i.e. "The attachment status flag was updated."). - my ($message) = (@_); - - # Retrieve a list of attachment status flags and create an array of hashes - # in which each hash contains the data for one flag. - SendSQL("SELECT attachstatusdefs.id, attachstatusdefs.name, " . - "attachstatusdefs.description, attachstatusdefs.sortkey, products.name, " . - "count(attachstatusdefs.id) " . - "FROM attachstatusdefs, products " . - "WHERE products.id = attachstatusdefs.product_id " . - "GROUP BY id " . - "ORDER BY attachstatusdefs.sortkey"); - my @statusdefs; - while ( MoreSQLData() ) - { - my ($id, $name, $description, $sortkey, $product, $attachcount) = FetchSQLData(); - push @statusdefs, { 'id' => $id , 'name' => $name , 'description' => $description , - 'sortkey' => $sortkey , 'product' => $product, - 'attachcount' => $attachcount }; - } - - # Define the variables and functions that will be passed to the UI template. - $vars->{'message'} = $message; - $vars->{'statusdefs'} = \@statusdefs; - - # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; - - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("admin/attachstatus/list.html.tmpl", $vars) - || ThrowTemplateError($template->error()); -} - - -sub create -{ - # Display a form for creating a new attachment status flag. - - # Retrieve a list of products to which the attachment status may apply. - SendSQL("SELECT name FROM products"); - my @products; - push(@products, FetchSQLData()) while MoreSQLData(); - - # Define the variables and functions that will be passed to the UI template. - $vars->{'products'} = \@products; - - # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; - - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("admin/attachstatus/create.html.tmpl", $vars) - || ThrowTemplateError($template->error()); -} - - -sub insert -{ - # Insert a new attachment status flag into the database. - - # Quote the flag's name and description as appropriate for inclusion - # in a SQL statement. - my $name = SqlQuote($::FORM{'name'}); - my $desc = SqlQuote($::FORM{'desc'}); - my $product_id = get_product_id($::FORM{'product'}); - - SendSQL("LOCK TABLES attachstatusdefs WRITE"); - SendSQL("SELECT MAX(id) FROM attachstatusdefs"); - my $id = FetchSQLData() + 1; - SendSQL("INSERT INTO attachstatusdefs (id, name, description, sortkey, product_id) - VALUES ($id, $name, $desc, $::FORM{'sortkey'}, $product_id)"); - SendSQL("UNLOCK TABLES"); - - # Display the "administer attachment status flags" page - # along with a message that the flag has been created. - list("The attachment status has been created."); -} - - -sub edit -{ - # Display a form for editing an existing attachment status flag. - - # Retrieve the definition from the database. - SendSQL("SELECT attachstatusdefs.name, attachstatusdefs.description, " . - " attachstatusdefs.sortkey, products.name " . - "FROM attachstatusdefs, products " . - "WHERE attachstatusdefs.product_id = products.id " . - " AND attachstatusdefs.id = $::FORM{'id'}"); - my ($name, $desc, $sortkey, $product) = FetchSQLData(); - - # Define the variables and functions that will be passed to the UI template. - $vars->{'id'} = $::FORM{'id'}; - $vars->{'name'} = $name; - $vars->{'desc'} = $desc; - $vars->{'sortkey'} = $sortkey; - $vars->{'product'} = $product; - - # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; - - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("admin/attachstatus/edit.html.tmpl", $vars) - || ThrowTemplateError($template->error()); -} - - -sub update -{ - # Update an attachment status flag in the database. - - # Quote the flag's name and description as appropriate for inclusion - # in a SQL statement. - my $name = SqlQuote($::FORM{'name'}); - my $desc = SqlQuote($::FORM{'desc'}); - - SendSQL("LOCK TABLES attachstatusdefs WRITE"); - SendSQL(" - UPDATE attachstatusdefs - SET name = $name , - description = $desc , - sortkey = $::FORM{'sortkey'} - WHERE id = $::FORM{'id'} - "); - SendSQL("UNLOCK TABLES"); - - # Display the "administer attachment status flags" page - # along with a message that the flag has been updated. - list("The attachment status has been updated."); -} - -sub confirmDelete -{ - # check if we need confirmation to delete: - - SendSQL("SELECT COUNT(attach_id), name - FROM attachstatusdefs LEFT JOIN attachstatuses - ON attachstatuses.statusid=attachstatusdefs.id - WHERE statusid = $::FORM{'id'} - GROUP BY attachstatuses.statusid;"); - - my ($attachcount, $name) = FetchSQLData(); - - if ($attachcount > 0) { - - $vars->{'id'} = $::FORM{'id'}; - $vars->{'attachcount'} = $attachcount; - $vars->{'name'} = $name; - - print "Content-type: text/html\n\n"; - - $template->process("admin/attachstatus/delete.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - } - else { - deleteStatus(); - } -} - -sub deleteStatus -{ - # Delete an attachment status flag from the database. - - SendSQL("LOCK TABLES attachstatusdefs WRITE, attachstatuses WRITE"); - SendSQL("DELETE FROM attachstatuses WHERE statusid = $::FORM{'id'}"); - SendSQL("DELETE FROM attachstatusdefs WHERE id = $::FORM{'id'}"); - SendSQL("UNLOCK TABLES"); - - # Display the "administer attachment status flags" page - # along with a message that the flag has been deleted. - list("The attachment status has been deleted."); -} diff --git a/editcomponents.cgi b/editcomponents.cgi index 7ad81ddfa..fc45b52c8 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -581,7 +581,9 @@ if ($action eq 'delete') { bugs WRITE, bugs_activity WRITE, components WRITE, - dependencies WRITE"); + dependencies WRITE, + flaginclusions WRITE, + flagexclusions WRITE"); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries @@ -610,6 +612,12 @@ if ($action eq 'delete') { print "Bugs deleted.
\n"; } + SendSQL("DELETE FROM flaginclusions + WHERE component_id=$component_id"); + SendSQL("DELETE FROM flagexclusions + WHERE component_id=$component_id"); + print "Flag inclusions and exclusions deleted.
\n"; + SendSQL("DELETE FROM components WHERE id=$component_id"); print "Components deleted.

\n"; diff --git a/editflagtypes.cgi b/editflagtypes.cgi new file mode 100755 index 000000000..aed73f284 --- /dev/null +++ b/editflagtypes.cgi @@ -0,0 +1,494 @@ +#!/usr/bonsaitools/bin/perl -wT +# -*- 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): Myk Melez + +################################################################################ +# Script Initialization +################################################################################ + +# Make it harder for us to do dangerous things in Perl. +use strict; +use lib "."; + +# Include the Bugzilla CGI and general utility library. +require "CGI.pl"; + +# Establish a connection to the database backend. +ConnectToDatabase(); + +# Use Bugzilla's flag modules for handling flag types. +use Bugzilla::Flag; +use Bugzilla::FlagType; + +use vars qw( $template $vars ); + +# Make sure the user is logged in and is an administrator. +confirm_login(); +UserInGroup("editcomponents") + || ThrowUserError("authorization_failure", + { action => "administer flag types" }); + +# Suppress "used only once" warnings. +use vars qw(@legal_product @legal_components %components); + +my $product_id; +my $component_id; + +################################################################################ +# Main Body Execution +################################################################################ + +# All calls to this script should contain an "action" variable whose value +# determines what the user wants to do. The code below checks the value of +# that variable and runs the appropriate code. + +# Determine whether to use the action specified by the user or the default. +my $action = $::FORM{'action'} || 'list'; + +if ($::FORM{'categoryAction'}) { + processCategoryChange(); + exit; +} + +if ($action eq 'list') { list(); } +elsif ($action eq 'enter') { edit(); } +elsif ($action eq 'copy') { edit(); } +elsif ($action eq 'edit') { edit(); } +elsif ($action eq 'insert') { insert(); } +elsif ($action eq 'update') { update(); } +elsif ($action eq 'confirmdelete') { confirmDelete(); } +elsif ($action eq 'delete') { &delete(); } +elsif ($action eq 'deactivate') { deactivate(); } +else { + ThrowCodeError("action_unrecognized", { action => $action }); +} + +exit; + +################################################################################ +# Functions +################################################################################ + +sub list { + # Define the variables and functions that will be passed to the UI template. + $vars->{'bug_types'} = Bugzilla::FlagType::match({ 'target_type' => 'bug' }, 1); + $vars->{'attachment_types'} = + Bugzilla::FlagType::match({ 'target_type' => 'attachment' }, 1); + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("admin/flag-type/list.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + + +sub edit { + $action eq 'enter' ? validateTargetType() : validateID(); + + # Get this installation's products and components. + GetVersionTable(); + + # products and components and the function used to modify the components + # menu when the products menu changes; used by the template to populate + # the menus and keep the components menu consistent with the products menu + $vars->{'products'} = \@::legal_product; + $vars->{'components'} = \@::legal_components; + $vars->{'components_by_product'} = \%::components; + + $vars->{'last_action'} = $::FORM{'action'}; + if ($::FORM{'action'} eq 'enter' || $::FORM{'action'} eq 'copy') { + $vars->{'action'} = "insert"; + } + else { + $vars->{'action'} = "update"; + } + + # If copying or editing an existing flag type, retrieve it. + if ($::FORM{'action'} eq 'copy' || $::FORM{'action'} eq 'edit') { + $vars->{'type'} = Bugzilla::FlagType::get($::FORM{'id'}); + $vars->{'type'}->{'inclusions'} = Bugzilla::FlagType::get_inclusions($::FORM{'id'}); + $vars->{'type'}->{'exclusions'} = Bugzilla::FlagType::get_exclusions($::FORM{'id'}); + } + # Otherwise set the target type (the minimal information about the type + # that the template needs to know) from the URL parameter. + else { + $vars->{'type'} = { 'target_type' => $::FORM{'target_type'} }; + } + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("admin/flag-type/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + +sub processCategoryChange { + validateIsActive(); + validateIsRequestable(); + validateIsRequesteeble(); + validateAllowMultiple(); + + my @inclusions = $::MFORM{'inclusions'} ? @{$::MFORM{'inclusions'}} : (); + my @exclusions = $::MFORM{'exclusions'} ? @{$::MFORM{'exclusions'}} : (); + if ($::FORM{'categoryAction'} eq "Include") { + validateProduct(); + validateComponent(); + my $category = ($::FORM{'product'} || "__Any__") . ":" . ($::FORM{'component'} || "__Any__"); + push(@inclusions, $category) unless grep($_ eq $category, @inclusions); + } + elsif ($::FORM{'categoryAction'} eq "Exclude") { + validateProduct(); + validateComponent(); + my $category = ($::FORM{'product'} || "__Any__") . ":" . ($::FORM{'component'} || "__Any__"); + push(@exclusions, $category) unless grep($_ eq $category, @exclusions); + } + elsif ($::FORM{'categoryAction'} eq "Remove Inclusion") { + @inclusions = map(($_ eq $::FORM{'inclusion_to_remove'} ? () : $_), @inclusions); + } + elsif ($::FORM{'categoryAction'} eq "Remove Exclusion") { + @exclusions = map(($_ eq $::FORM{'exclusion_to_remove'} ? () : $_), @exclusions); + } + + # Get this installation's products and components. + GetVersionTable(); + + # products and components; used by the template to populate the menus + # and keep the components menu consistent with the products menu + $vars->{'products'} = \@::legal_product; + $vars->{'components'} = \@::legal_components; + $vars->{'components_by_product'} = \%::components; + + $vars->{'action'} = $::FORM{'action'}; + my $type = {}; + foreach my $key (keys %::FORM) { $type->{$key} = $::FORM{$key} } + $type->{'inclusions'} = \@inclusions; + $type->{'exclusions'} = \@exclusions; + $vars->{'type'} = $type; + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("admin/flag-type/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + +sub insert { + validateName(); + validateDescription(); + validateCCList(); + validateTargetType(); + validateSortKey(); + validateIsActive(); + validateIsRequestable(); + validateIsRequesteeble(); + validateAllowMultiple(); + + my $name = SqlQuote($::FORM{'name'}); + my $description = SqlQuote($::FORM{'description'}); + my $cc_list = SqlQuote($::FORM{'cc_list'}); + my $target_type = $::FORM{'target_type'} eq "bug" ? "b" : "a"; + + SendSQL("LOCK TABLES flagtypes WRITE, products READ, components READ, " . + "flaginclusions WRITE, flagexclusions WRITE"); + + # Determine the new flag type's unique identifier. + SendSQL("SELECT MAX(id) FROM flagtypes"); + my $id = FetchSQLData() + 1; + + # Insert a record for the new flag type into the database. + SendSQL("INSERT INTO flagtypes (id, name, description, cc_list, + target_type, sortkey, is_active, is_requestable, + is_requesteeble, is_multiplicable) + VALUES ($id, $name, $description, $cc_list, '$target_type', + $::FORM{'sortkey'}, $::FORM{'is_active'}, + $::FORM{'is_requestable'}, $::FORM{'is_requesteeble'}, + $::FORM{'is_multiplicable'})"); + + # Populate the list of inclusions/exclusions for this flag type. + foreach my $category_type ("inclusions", "exclusions") { + foreach my $category (@{$::MFORM{$category_type}}) { + my ($product, $component) = split(/:/, $category); + my $product_id = get_product_id($product) || "NULL"; + my $component_id = + get_component_id($product_id, $component) || "NULL"; + SendSQL("INSERT INTO flag$category_type (type_id, product_id, " . + "component_id) VALUES ($id, $product_id, $component_id)"); + } + } + + SendSQL("UNLOCK TABLES"); + + $vars->{'name'} = $::FORM{'name'}; + $vars->{'message'} = "flag_type_created"; + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("global/message.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + + +sub update { + validateID(); + validateName(); + validateDescription(); + validateCCList(); + validateTargetType(); + validateSortKey(); + validateIsActive(); + validateIsRequestable(); + validateIsRequesteeble(); + validateAllowMultiple(); + + my $name = SqlQuote($::FORM{'name'}); + my $description = SqlQuote($::FORM{'description'}); + my $cc_list = SqlQuote($::FORM{'cc_list'}); + + SendSQL("LOCK TABLES flagtypes WRITE, products READ, components READ, " . + "flaginclusions WRITE, flagexclusions WRITE"); + SendSQL("UPDATE flagtypes + SET name = $name , + description = $description , + cc_list = $cc_list , + sortkey = $::FORM{'sortkey'} , + is_active = $::FORM{'is_active'} , + is_requestable = $::FORM{'is_requestable'} , + is_requesteeble = $::FORM{'is_requesteeble'} , + is_multiplicable = $::FORM{'is_multiplicable'} + WHERE id = $::FORM{'id'}"); + + # Update the list of inclusions/exclusions for this flag type. + foreach my $category_type ("inclusions", "exclusions") { + SendSQL("DELETE FROM flag$category_type WHERE type_id = $::FORM{'id'}"); + foreach my $category (@{$::MFORM{$category_type}}) { + my ($product, $component) = split(/:/, $category); + my $product_id = get_product_id($product) || "NULL"; + my $component_id = + get_component_id($product_id, $component) || "NULL"; + SendSQL("INSERT INTO flag$category_type (type_id, product_id, " . + "component_id) VALUES ($::FORM{'id'}, $product_id, " . + "$component_id)"); + } + } + + SendSQL("UNLOCK TABLES"); + + # Clear existing flags for bugs/attachments in categories no longer on + # the list of inclusions or that have been added to the list of exclusions. + SendSQL(" + SELECT flags.id + FROM flags, bugs LEFT OUTER JOIN flaginclusions AS i + ON (flags.type_id = i.type_id + AND (bugs.product_id = i.product_id OR i.product_id IS NULL) + AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) + WHERE flags.type_id = $::FORM{'id'} + AND flags.bug_id = bugs.bug_id + AND i.type_id IS NULL + "); + Bugzilla::Flag::clear(FetchOneColumn()) while MoreSQLData(); + + SendSQL(" + SELECT flags.id + FROM flags, bugs, flagexclusions AS e + WHERE flags.type_id = $::FORM{'id'} + AND flags.bug_id = bugs.bug_id + AND flags.type_id = e.type_id + AND (bugs.product_id = e.product_id OR e.product_id IS NULL) + AND (bugs.component_id = e.component_id OR e.component_id IS NULL) + "); + Bugzilla::Flag::clear(FetchOneColumn()) while MoreSQLData(); + + $vars->{'name'} = $::FORM{'name'}; + $vars->{'message'} = "flag_type_changes_saved"; + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("global/message.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + + +sub confirmDelete +{ + validateID(); + # check if we need confirmation to delete: + + my $count = Bugzilla::Flag::count({ 'type_id' => $::FORM{'id'} }); + + if ($count > 0) { + $vars->{'flag_type'} = Bugzilla::FlagType::get($::FORM{'id'}); + $vars->{'flag_count'} = scalar($count); + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("admin/flag-type/confirm-delete.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + else { + deleteType(); + } +} + + +sub delete { + validateID(); + + SendSQL("LOCK TABLES flagtypes WRITE, flags WRITE, " . + "flaginclusions WRITE, flagexclusions WRITE"); + + # Get the name of the flag type so we can tell users + # what was deleted. + SendSQL("SELECT name FROM flagtypes WHERE id = $::FORM{'id'}"); + $vars->{'name'} = FetchOneColumn(); + + SendSQL("DELETE FROM flags WHERE type_id = $::FORM{'id'}"); + SendSQL("DELETE FROM flaginclusions WHERE type_id = $::FORM{'id'}"); + SendSQL("DELETE FROM flagexclusions WHERE type_id = $::FORM{'id'}"); + SendSQL("DELETE FROM flagtypes WHERE id = $::FORM{'id'}"); + SendSQL("UNLOCK TABLES"); + + $vars->{'message'} = "flag_type_deleted"; + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("global/message.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + + +sub deactivate { + validateID(); + validateIsActive(); + + SendSQL("LOCK TABLES flagtypes WRITE"); + SendSQL("UPDATE flagtypes SET is_active = 0 WHERE id = $::FORM{'id'}"); + SendSQL("UNLOCK TABLES"); + + $vars->{'message'} = "flag_type_deactivated"; + $vars->{'flag_type'} = Bugzilla::FlagType::get($::FORM{'id'}); + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("global/message.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + + +################################################################################ +# Data Validation / Security Authorization +################################################################################ + +sub validateID { + detaint_natural($::FORM{'id'}) + || ThrowCodeError("flag_type_id_invalid", { id => $::FORM{'id'} }); + + SendSQL("SELECT 1 FROM flagtypes WHERE id = $::FORM{'id'}"); + FetchOneColumn() + || ThrowCodeError("flag_type_nonexistent", { id => $::FORM{'id'} }); +} + +sub validateName { + $::FORM{'name'} + && length($::FORM{'name'}) <= 50 + || ThrowUserError("flag_type_name_invalid", { name => $::FORM{'name'} }); +} + +sub validateDescription { + length($::FORM{'description'}) < 2^16-1 + || ThrowUserError("flag_type_description_invalid"); +} + +sub validateCCList { + length($::FORM{'cc_list'}) <= 200 + || ThrowUserError("flag_type_cc_list_invalid", + { cc_list => $::FORM{'cc_list'} }); + + my @addresses = split(/[, ]+/, $::FORM{'cc_list'}); + foreach my $address (@addresses) { CheckEmailSyntax($address) } +} + +sub validateProduct { + return if !$::FORM{'product'}; + + $product_id = get_product_id($::FORM{'product'}); + + defined($product_id) + || ThrowCodeError("flag_type_product_nonexistent", + { product => $::FORM{'product'} }); +} + +sub validateComponent { + return if !$::FORM{'component'}; + + $product_id + || ThrowCodeError("flag_type_component_without_product"); + + $component_id = get_component_id($product_id, $::FORM{'component'}); + + defined($component_id) + || ThrowCodeError("flag_type_component_nonexistent", + { product => $::FORM{'product'}, + component => $::FORM{'component'} }); +} + +sub validateSortKey { + detaint_natural($::FORM{'sortkey'}) + && $::FORM{'sortkey'} < 32768 + || ThrowUserError("flag_type_sortkey_invalid", + { sortkey => $::FORM{'sortkey'} }); +} + +sub validateTargetType { + grep($::FORM{'target_type'} eq $_, ("bug", "attachment")) + || ThrowCodeError("flag_type_target_type_invalid", + { target_type => $::FORM{'target_type'} }); +} + +sub validateIsActive { + $::FORM{'is_active'} = $::FORM{'is_active'} ? 1 : 0; +} + +sub validateIsRequestable { + $::FORM{'is_requestable'} = $::FORM{'is_requestable'} ? 1 : 0; +} + +sub validateIsRequesteeble { + $::FORM{'is_requesteeble'} = $::FORM{'is_requesteeble'} ? 1 : 0; +} + +sub validateAllowMultiple { + $::FORM{'is_multiplicable'} = $::FORM{'is_multiplicable'} ? 1 : 0; +} + diff --git a/editproducts.cgi b/editproducts.cgi index 18ad4216d..3db7c6f84 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -539,7 +539,9 @@ if ($action eq 'delete') { products WRITE, groups WRITE, profiles WRITE, - milestones WRITE"); + milestones WRITE, + flaginclusions WRITE, + flagexclusions WRITE); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries @@ -581,6 +583,12 @@ if ($action eq 'delete') { WHERE product_id=$product_id"); print "Milestones deleted.
\n"; + SendSQL("DELETE FROM flaginclusions + WHERE product_id=$product_id"); + SendSQL("DELETE FROM flagexclusions + WHERE product_id=$product_id"); + print "Flag inclusions and exclusions deleted.
\n"; + SendSQL("DELETE FROM products WHERE id=$product_id"); print "Product '$product' deleted.
\n"; diff --git a/globals.pl b/globals.pl index a6a751562..9a625a842 100644 --- a/globals.pl +++ b/globals.pl @@ -300,7 +300,12 @@ sub FetchOneColumn { "status", "resolution", "summary"); sub AppendComment { - my ($bugid,$who,$comment,$isprivate) = (@_); + my ($bugid, $who, $comment, $isprivate, $timestamp) = @_; + + # Use the date/time we were given if possible (allowing calling code + # to synchronize the comment's timestamp with those of other records). + $timestamp = ($timestamp ? SqlQuote($timestamp) : "NOW()"); + $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. @@ -310,7 +315,7 @@ sub AppendComment { my $whoid = DBNameToIdAndCheck($who); my $privacyval = $isprivate ? 1 : 0 ; SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext, isprivate) " . - "VALUES($bugid, $whoid, now(), " . SqlQuote($comment) . ", " . + "VALUES($bugid, $whoid, $timestamp, " . SqlQuote($comment) . ", " . $privacyval . ")"); SendSQL("UPDATE bugs SET delta_ts = now() WHERE bug_id = $bugid"); @@ -902,8 +907,7 @@ sub get_product_name { sub get_component_id { my ($prod_id, $comp) = @_; - die "non-numeric prod_id '$prod_id' passed to get_component_id" - unless ($prod_id =~ /^\d+$/); + return undef unless ($prod_id =~ /^\d+$/); PushGlobalSQLState(); SendSQL("SELECT id FROM components " . "WHERE product_id = $prod_id AND name = " . SqlQuote($comp)); diff --git a/process_bug.cgi b/process_bug.cgi index 4ddfcca2c..076e014fc 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -36,6 +36,9 @@ require "bug_form.pl"; use RelationSet; +# Use the Flag module to modify flag data if the user set flags. +use Bugzilla::Flag; + # Shut up misguided -w warnings about "used only once": use vars qw(%versions @@ -1052,8 +1055,9 @@ foreach my $id (@idlist) { "profiles $write, dependencies $write, votes $write, " . "products READ, components READ, " . "keywords $write, longdescs $write, fielddefs $write, " . - "bug_group_map $write, " . - "user_group_map READ, " . + "bug_group_map $write, flags $write, " . + "user_group_map READ, flagtypes READ, " . + "flaginclusions AS i READ, flagexclusions AS e READ, " . "keyworddefs READ, groups READ, attachments READ"); my @oldvalues = SnapShotBug($id); my %oldhash; @@ -1238,7 +1242,7 @@ foreach my $id (@idlist) { LogActivityEntry($id, "bug_group", $groupDelNames, $groupAddNames); if (defined $::FORM{'comment'}) { AppendComment($id, $::COOKIE{'Bugzilla_login'}, $::FORM{'comment'}, - $::FORM{'commentprivacy'}); + $::FORM{'commentprivacy'}, $timestamp); } my $removedCcString = ""; @@ -1399,6 +1403,14 @@ foreach my $id (@idlist) { # what has changed since before we wrote out the new values. # my @newvalues = SnapShotBug($id); + my %newhash; + $i = 0; + foreach my $col (@::log_columns) { + # Consider NULL db entries to be equivalent to the empty string + $newvalues[$i] ||= ''; + $newhash{$col} = $newvalues[$i]; + $i++; + } # for passing to processmail to ensure that when someone is removed # from one of these fields, they get notified of that fact (if desired) @@ -1411,12 +1423,6 @@ foreach my $id (@idlist) { # values in place. my $old = shift @oldvalues; my $new = shift @newvalues; - if (!defined $old) { - $old = ""; - } - if (!defined $new) { - $new = ""; - } if ($old ne $new) { # Products and components are now stored in the DB using ID's @@ -1461,6 +1467,11 @@ foreach my $id (@idlist) { LogActivityEntry($id,$col,$old,$new); } } + # Set and update flags. + if ($UserInEditGroupSet) { + my $target = Bugzilla::Flag::GetTarget($id); + Bugzilla::Flag::process($target, $timestamp, \%::FORM); + } if ($bug_changed) { SendSQL("UPDATE bugs SET delta_ts = " . SqlQuote($timestamp) . " WHERE bug_id = $id"); } diff --git a/productmenu.js b/productmenu.js new file mode 100644 index 000000000..d917d325c --- /dev/null +++ b/productmenu.js @@ -0,0 +1,242 @@ +// Adds to the target select object all elements in array that +// correspond to the elements selected in source. +// - array should be a array of arrays, indexed by product name. the +// array should contain the elements that correspont to that +// product. Example: +// var array = Array(); +// array['ProductOne'] = [ 'ComponentA', 'ComponentB' ]; +// updateSelect(array, source, target); +// - sel is a list of selected items, either whole or a diff +// depending on sel_is_diff. +// - sel_is_diff determines if we are sending in just a diff or the +// whole selection. a diff is used to optimize adding selections. +// - target should be the target select object. +// - single specifies if we selected a single item. if we did, no +// need to merge. + +function updateSelect( array, sel, target, sel_is_diff, single, blank ) { + + var i, j, comp; + + // if single, even if it's a diff (happens when you have nothing + // selected and select one item alone), skip this. + if ( ! single ) { + + // array merging/sorting in the case of multiple selections + if ( sel_is_diff ) { + + // merge in the current options with the first selection + comp = merge_arrays( array[sel[0]], target.options, 1 ); + + // merge the rest of the selection with the results + for ( i = 1 ; i < sel.length ; i++ ) { + comp = merge_arrays( array[sel[i]], comp, 0 ); + } + } else { + // here we micro-optimize for two arrays to avoid merging with a + // null array + comp = merge_arrays( array[sel[0]],array[sel[1]], 0 ); + + // merge the arrays. not very good for multiple selections. + for ( i = 2; i < sel.length; i++ ) { + comp = merge_arrays( comp, array[sel[i]], 0 ); + } + } + } else { + // single item in selection, just get me the list + comp = array[sel[0]]; + } + + // save the selection in the target select so we can restore it later + var selections = new Array(); + for ( i = 0; i < target.options.length; i++ ) + if (target.options[i].selected) selections.push(target.options[i].value); + + // clear select + target.options.length = 0; + + // add empty "Any" value back to the list + if (blank) target.options[0] = new Option( blank, "" ); + + // load elements of list into select + for ( i = 0; i < comp.length; i++ ) { + target.options[target.options.length] = new Option( comp[i], comp[i] ); + } + + // restore the selection + for ( i=0 ; i bitem.toLowerCase() ) { + ret[ret.length] = bitem; + pos_b++; + } else { + // list contents are equal, inc both counters. + ret[ret.length] = aitem; + pos_a++; + pos_b++; + } + } + } + + // catch leftovers here. these sections are ugly code-copying. + if ( pos_a < a.length ) { + for ( ; pos_a < a.length ; pos_a++ ) { + ret[ret.length] = a[pos_a]; + } + } + + if ( pos_b < b.length ) { + for ( ; pos_b < b.length; pos_b++ ) { + if ( b_is_select ) { + bitem = b[pos_b].value; + } else { + bitem = b[pos_b]; + } + ret[ret.length] = bitem; + } + } + return ret; + } + +// selectProduct reads the selection from f[productfield] and updates +// f.version, component and target_milestone accordingly. +// - f: a form containing product, component, varsion and +// target_milestone select boxes. +// globals (3vil!): +// - cpts, vers, tms: array of arrays, indexed by product name. the +// subarrays contain a list of names to be fed to the respective +// selectboxes. For bugzilla, these are generated with perl code +// at page start. +// - usetms: this is a global boolean that is defined if the +// bugzilla installation has it turned on. generated in perl too. +// - first_load: boolean, specifying if it's the first time we load +// the query page. +// - last_sel: saves our last selection list so we know what has +// changed, and optimize for additions. + +function selectProduct( f , productfield, componentfield, blank ) { + + // this is to avoid handling events that occur before the form + // itself is ready, which happens in buggy browsers. + + if ( ( !f ) || ( ! f[productfield] ) ) { + return; + } + + // if this is the first load and nothing is selected, no need to + // merge and sort all components; perl gives it to us sorted. + + if ( ( first_load ) && ( f[productfield].selectedIndex == -1 ) ) { + first_load = 0; + return; + } + + // turn first_load off. this is tricky, since it seems to be + // redundant with the above clause. It's not: if when we first load + // the page there is _one_ element selected, it won't fall into that + // clause, and first_load will remain 1. Then, if we unselect that + // item, selectProduct will be called but the clause will be valid + // (since selectedIndex == -1), and we will return - incorrectly - + // without merge/sorting. + + first_load = 0; + + // - sel keeps the array of products we are selected. + // - is_diff says if it's a full list or just a list of products that + // were added to the current selection. + // - single indicates if a single item was selected + var sel = Array(); + var is_diff = 0; + var single; + + // if nothing selected, pick all + if ( f[productfield].selectedIndex == -1 ) { + for ( var i = 0 ; i < f[productfield].length ; i++ ) { + sel[sel.length] = f[productfield].options[i].value; + } + single = 0; + } else { + + for ( i = 0 ; i < f[productfield].length ; i++ ) { + if ( f[productfield].options[i].selected ) { + sel[sel.length] = f[productfield].options[i].value; + } + } + + single = ( sel.length == 1 ); + + // save last_sel before we kill it + var tmp = last_sel; + last_sel = sel; + + // this is an optimization: if we've added components, no need + // to remerge them; just merge the new ones with the existing + // options. + + if ( ( tmp ) && ( tmp.length < sel.length ) ) { + sel = fake_diff_array(sel, tmp); + is_diff = 1; + } + } + + // do the actual fill/update + updateSelect( cpts, sel, f[componentfield], is_diff, single, blank ); +} diff --git a/request.cgi b/request.cgi new file mode 100755 index 000000000..eb365559e --- /dev/null +++ b/request.cgi @@ -0,0 +1,279 @@ +#!/usr/bonsaitools/bin/perl -wT +# -*- 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): Myk Melez + +################################################################################ +# Script Initialization +################################################################################ + +# Make it harder for us to do dangerous things in Perl. +use diagnostics; +use strict; + +# Include the Bugzilla CGI and general utility library. +use lib qw(.); +require "CGI.pl"; + +# Establish a connection to the database backend. +ConnectToDatabase(); + +# Use Bugzilla's Request module which contains utilities for handling requests. +use Bugzilla::Flag; +use Bugzilla::FlagType; + +# use Bugzilla's User module which contains utilities for handling users. +use Bugzilla::User; + +use vars qw($template $vars @legal_product @legal_components %components); + +# Make sure the user is logged in. +quietly_check_login(); + +################################################################################ +# Main Body Execution +################################################################################ + +queue(); +exit; + +################################################################################ +# Functions +################################################################################ + +sub queue { + validateStatus(); + validateGroup(); + + my $attach_join_clause = "flags.attach_id = attachments.attach_id"; + if (Param("insidergroup") && !UserInGroup(Param("insidergroup"))) { + $attach_join_clause .= " AND attachment.isprivate < 1"; + } + + my $query = + # Select columns describing each flag, the bug/attachment on which + # it has been set, who set it, and of whom they are requesting it. + " SELECT flags.id, flagtypes.name, + flags.status, + flags.bug_id, bugs.short_desc, + products.name, components.name, + flags.attach_id, attachments.description, + requesters.realname, requesters.login_name, + requestees.realname, requestees.login_name, + flags.creation_date, + " . + # Select columns that help us weed out secure bugs to which the user + # should not have access. + " COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, + COUNT(DISTINCT bgmap.group_id) AS cntbugingroups, + ((COUNT(DISTINCT ccmap.who) AND cclist_accessible) + OR ((bugs.reporter = $::userid) AND bugs.reporter_accessible) + OR bugs.assigned_to = $::userid ) AS canseeanyway + " . + # Use the flags and flagtypes tables for information about the flags, + # the bugs and attachments tables for target info, the profiles tables + # for setter and requestee info, the products/components tables + # so we can display product and component names, and the bug_group_map + # and user_group_map tables to help us weed out secure bugs to which + # the user should not have access. + " FROM flags + LEFT JOIN attachments ON ($attach_join_clause), + flagtypes, + profiles AS requesters + LEFT JOIN profiles AS requestees + ON flags.requestee_id = requestees.userid, + bugs + LEFT JOIN products ON bugs.product_id = products.id + LEFT JOIN components ON bugs.component_id = components.id + LEFT JOIN bug_group_map AS bgmap + ON bgmap.bug_id = bugs.bug_id + LEFT JOIN user_group_map AS ugmap + ON bgmap.group_id = ugmap.group_id + AND ugmap.user_id = $::userid + AND ugmap.isbless = 0 + LEFT JOIN cc AS ccmap + ON ccmap.who = $::userid AND ccmap.bug_id = bugs.bug_id + " . + # All of these are inner join clauses. Actual match criteria are added + # in the code below. + " WHERE flags.type_id = flagtypes.id + AND flags.setter_id = requesters.userid + AND flags.bug_id = bugs.bug_id + "; + + # A list of columns to exclude from the report because the report conditions + # limit the data being displayed to exact matches for those columns. + # In other words, if we are only displaying "pending" , we don't + # need to display a "status" column in the report because the value for that + # column will always be the same. + my @excluded_columns = (); + + # Filter requests by status: "pending", "granted", "denied", "all" + # (which means any), or "fulfilled" (which means "granted" or "denied"). + $::FORM{'status'} ||= "?"; + if ($::FORM{'status'} eq "+-") { + $query .= " AND flags.status IN ('+', '-')"; + } + elsif ($::FORM{'status'} ne "all") { + $query .= " AND flags.status = '$::FORM{'status'}'"; + push(@excluded_columns, 'status'); + } + + # Filter results by exact email address of requester or requestee. + if (defined($::FORM{'requester'}) && $::FORM{'requester'} ne "") { + $query .= " AND requesters.login_name = " . SqlQuote($::FORM{'requester'}); + push(@excluded_columns, 'requester'); + } + if (defined($::FORM{'requestee'}) && $::FORM{'requestee'} ne "") { + $query .= " AND requestees.login_name = " . SqlQuote($::FORM{'requestee'}); + push(@excluded_columns, 'requestee'); + } + + # Filter results by exact product or component. + if (defined($::FORM{'product'}) && $::FORM{'product'} ne "") { + my $product_id = get_product_id($::FORM{'product'}); + if ($product_id) { + $query .= " AND bugs.product_id = $product_id"; + push(@excluded_columns, 'product'); + if (defined($::FORM{'component'}) && $::FORM{'component'} ne "") { + my $component_id = get_component_id($product_id, $::FORM{'component'}); + if ($component_id) { + $query .= " AND bugs.component_id = $component_id"; + push(@excluded_columns, 'component'); + } + else { ThrowCodeError("unknown_component", { %::FORM }) } + } + } + else { ThrowCodeError("unknown_product", { %::FORM }) } + } + + # Filter results by flag types. + if (defined($::FORM{'type'}) && !grep($::FORM{'type'} eq $_, ("", "all"))) { + # Check if any matching types are for attachments. If not, don't show + # the attachment column in the report. + my $types = Bugzilla::FlagType::match({ 'name' => $::FORM{'type'} }); + my $has_attachment_type = 0; + foreach my $type (@$types) { + if ($type->{'target_type'} eq "attachment") { + $has_attachment_type = 1; + last; + } + } + if (!$has_attachment_type) { push(@excluded_columns, 'attachment') } + + $query .= " AND flagtypes.name = " . SqlQuote($::FORM{'type'}); + push(@excluded_columns, 'type'); + } + + # Group the records by flag ID so we don't get multiple rows of data + # for each flag. This is only necessary because of the code that + # removes flags on bugs the user is unauthorized to access. + $query .= " GROUP BY flags.id " . + "HAVING cntuseringroups = cntbugingroups OR canseeanyway "; + + # Group the records, in other words order them by the group column + # so the loop in the display template can break them up into separate + # tables every time the value in the group column changes. + $::FORM{'group'} ||= "requestee"; + if ($::FORM{'group'} eq "requester") { + $query .= " ORDER BY requesters.realname, requesters.login_name"; + } + elsif ($::FORM{'group'} eq "requestee") { + $query .= " ORDER BY requestees.realname, requestees.login_name"; + } + elsif ($::FORM{'group'} eq "category") { + $query .= " ORDER BY products.name, components.name"; + } + elsif ($::FORM{'group'} eq "type") { + $query .= " ORDER BY flagtypes.name"; + } + + # Order the records (within each group). + $query .= " , flags.creation_date"; + + # Pass the query to the template for use when debugging this script. + $vars->{'query'} = $query; + + SendSQL($query); + my @requests = (); + while (MoreSQLData()) { + my @data = FetchSQLData(); + my $request = { + 'id' => $data[0] , + 'type' => $data[1] , + 'status' => $data[2] , + 'bug_id' => $data[3] , + 'bug_summary' => $data[4] , + 'category' => "$data[5]: $data[6]" , + 'attach_id' => $data[7] , + 'attach_summary' => $data[8] , + 'requester' => ($data[9] ? "$data[9] <$data[10]>" : $data[10]) , + 'requestee' => ($data[11] ? "$data[11] <$data[12]>" : $data[12]) , + 'created' => $data[13] + }; + push(@requests, $request); + } + + # Get a list of request type names to use in the filter form. + my @types = ("all"); + SendSQL("SELECT DISTINCT(name) FROM flagtypes ORDER BY name"); + push(@types, FetchOneColumn()) while MoreSQLData(); + + # products and components and the function used to modify the components + # menu when the products menu changes; used by the template to populate + # the menus and keep the components menu consistent with the products menu + GetVersionTable(); + $vars->{'products'} = \@::legal_product; + $vars->{'components'} = \@::legal_components; + $vars->{'components_by_product'} = \%::components; + + $vars->{'excluded_columns'} = \@excluded_columns; + $vars->{'group_field'} = $::FORM{'group'}; + $vars->{'requests'} = \@requests; + $vars->{'form'} = \%::FORM; + $vars->{'types'} = \@types; + + # Return the appropriate HTTP response headers. + print "Content-type: text/html\n\n"; + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("request/queue.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + +################################################################################ +# Data Validation / Security Authorization +################################################################################ + +sub validateStatus { + return if !defined($::FORM{'status'}); + + grep($::FORM{'status'} eq $_, qw(? +- + - all)) + || ThrowCodeError("flag_status_invalid", { status => $::FORM{'status'} }); +} + +sub validateGroup { + return if !defined($::FORM{'group'}); + + grep($::FORM{'group'} eq $_, qw(requester requestee category type)) + || ThrowCodeError("request_queue_group_invalid", + { group => $::FORM{'group'} }); +} + diff --git a/sanitycheck.cgi b/sanitycheck.cgi index da71163cc..2798dfd2f 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -232,11 +232,11 @@ CrossCheck("fielddefs", "fieldid", ["bugs_activity", "fieldid"]); CrossCheck("attachments", "attach_id", - ["attachstatuses", "attach_id"], + ["flags", "attach_id"], ["bugs_activity", "attach_id"]); -CrossCheck("attachstatusdefs", "id", - ["attachstatuses", "statusid"]); +CrossCheck("flagtypes", "id", + ["flags", "type_id"]); CrossCheck("bugs", "bug_id", ["bugs_activity", "bug_id"], @@ -280,7 +280,7 @@ CrossCheck("products", "id", ["components", "product_id", "name"], ["milestones", "product_id", "value"], ["versions", "product_id", "value"], - ["attachstatusdefs", "product_id", "name"]); + ["flagtypes", "product_id", "name"]); DateCheck("groups", "last_changed"); DateCheck("profiles", "refreshed_when"); diff --git a/template/en/default/account/prefs/email.html.tmpl b/template/en/default/account/prefs/email.html.tmpl index e14ea9910..5d73a357b 100644 --- a/template/en/default/account/prefs/email.html.tmpl +++ b/template/en/default/account/prefs/email.html.tmpl @@ -83,9 +83,27 @@

+ + + + + + + + diff --git a/template/en/default/admin/flag-type/confirm-delete.html.tmpl b/template/en/default/admin/flag-type/confirm-delete.html.tmpl new file mode 100644 index 000000000..b022e621e --- /dev/null +++ b/template/en/default/admin/flag-type/confirm-delete.html.tmpl @@ -0,0 +1,58 @@ + +[%# 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): Myk Melez + #%] + +[%# Filter off the name here to be used multiple times below %] +[% name = BLOCK %][% flag_type.name FILTER html %][% END %] + +[% PROCESS global/header.html.tmpl + title = "Confirm Deletion of Flag Type '$name'" +%] + +

+ There are [% flag_count %] flags of type [% name %]. + If you delete this type, those flags will also be deleted. Note that + instead of deleting the type you can + deactivate it, + in which case the type and its flags will remain in the database + but will not appear in the Bugzilla UI. +

+ +
[% IF has_bits.size %] You have the following permission bits set on your account: -
    +

    +
    + [% FOREACH bit_description = has_bits %] -
  • [% bit_description %]
  • + + + + [% END %] - +
    [% bit_description.name %][% bit_description.desc %]
    [% ELSE %] There are no permission bits set on your account. [% END %] [% IF set_bits.size %] +
    And you can turn on or off the following bits for other users:

    -

      + [% FOREACH bit_description = set_bits %] -
    • [% bit_description %]
    • + + + + [% END %] - +
      [% bit_description.name %][% bit_description.desc %]

      [% END %]
- + +
+
+ + +
+
+ +
+ + + + + + + +
+ Do you really want to delete this type? +
+ + Yes, delete + + + + No, don't delete + +
+ +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/flag-type/edit.html.tmpl b/template/en/default/admin/flag-type/edit.html.tmpl new file mode 100644 index 000000000..ca01f6365 --- /dev/null +++ b/template/en/default/admin/flag-type/edit.html.tmpl @@ -0,0 +1,189 @@ +[%# 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): Myk Melez + #%] + +[%# The javascript and header_html blocks get used in header.html.tmpl. %] +[% javascript = BLOCK %] + var usetms = 0; // do we have target milestone? + var first_load = 1; // is this the first time we load the page? + var last_sel = []; // caches last selection + var cpts = new Array(); + [% FOREACH p = products %] + cpts['[% p FILTER js %]'] = [ + [%- FOREACH item = components_by_product.$p %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ]; + [% END %] +[% END %] + +[% header_html = BLOCK %] + +[% END %] + +[% IF type.target_type == "bug" %] + [% title = "Create Flag Type for Bugs" %] +[% ELSE %] + [% title = "Create Flag Type for Attachments" %] +[% END %] + +[% IF last_action == "copy" %] + [% title = "Create Flag Type Based on $type.name" %] +[% ELSIF last_action == "edit" %] + [% title = "Edit Flag Type $type.name" %] +[% END %] + +[% PROCESS global/header.html.tmpl + title = title + style = " + table#form th { text-align: right; vertical-align: baseline; white-space: nowrap; } + table#form td { text-align: left; vertical-align: baseline; } + " + onload="selectProduct(forms[0], 'product', 'component', '__Any__');" +%] + +

+ + + + [% FOREACH category = type.inclusions %] + + [% END %] + [% FOREACH category = type.exclusions %] + + [% END %] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name: + a short name identifying this type
+ +
Description: + a comprehensive description of this type
+ +
Category: + the products/components to which [% type.target_type %]s must + (inclusions) or must not (exclusions) belong in order for users + to be able to set flags of this type for them + + + + + + +
+ Product/Component:
+
+
+ + +
+ Inclusions:
+ [% PROCESS "global/select-menu.html.tmpl" name="inclusion_to_remove" multiple=1 size=4 options=type.inclusions %]
+ +
+ Exclusions:
+ [% PROCESS "global/select-menu.html.tmpl" name="exclusion_to_remove" multiple=1 size=4 options=type.exclusions %]
+ +
+
Sort Key: + a number between 1 and 32767 by which this type will be sorted + when displayed to users in a list; ignore if you don't care + what order the types appear in or if you want them to appear + in alphabetical order
+ +
  + + active (flags of this type appear in the UI and can be set) +
  + + requestable (users can ask for flags of this type to be set) +
CC List: + if requestable, who should get carbon copied on email notification of requests
+ +
  + + specifically requestable (users can ask specific other users to set flags of this type as opposed to just asking the wind) +
  + + multiplicable (multiple flags of this type can be set on the same [% type.target_type %]) +
+ +
+ +
+ +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/flag-type/list.html.tmpl b/template/en/default/admin/flag-type/list.html.tmpl new file mode 100644 index 000000000..76a835639 --- /dev/null +++ b/template/en/default/admin/flag-type/list.html.tmpl @@ -0,0 +1,107 @@ +[%# 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): Myk Melez + #%] + +[% PROCESS global/header.html.tmpl + title = 'Administer Flag Types' + style = " + table#flag_types tr th { text-align: left; } + .inactive { color: #787878; } + " +%] + +

+ Flags are markers that identify whether a bug or attachment has been granted + or denied some status. Flags appear in the UI as a name and a status symbol + ("+" for granted, "-" for denied, and "?" for statuses requested by users). +

+ +

+ For example, you might define a "review" status for users to request review + for their patches. When a patch writer requests review, the string "review?" + will appear in the attachment. When a patch reviewer reviews the patch, + either the string "review+" or the string "review-" will appear in the patch, + depending on whether the patch passed or failed review. +

+ +

Flag Types for Bugs

+ +[% PROCESS display_flag_types types=bug_types %] + +

+ Create Flag Type for Bugs +

+ +

Flag Types for Attachments

+ +[% PROCESS display_flag_types types=attachment_types %] + +

+ Create Flag Type For Attachments +

+ + + +[% PROCESS global/footer.html.tmpl %] + + +[% BLOCK display_flag_types %] + + + + + + + + + [% FOREACH type = types %] + + + + + + + + [% END %] + +
NameDescriptionActions
[% type.name FILTER html %][% type.description FILTER html %] + Edit + | Copy + | Delete +
+[% END %] diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index ec2616bf9..32449f041 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -32,6 +32,8 @@ table.attachment_info th { text-align: right; vertical-align: top; } table.attachment_info td { text-align: left; vertical-align: top; } #noview { text-align: left; vertical-align: center; } + + table#flags th, table#flags td { font-size: small; vertical-align: baseline; } " %] @@ -158,8 +160,7 @@ MIME Type:

- - Flags:
+ @@ -168,20 +169,14 @@
[% IF (Param("insidergroup") && UserInGroup(Param("insidergroup"))) %] private

+ [% ELSE %]
[% END %] - [% IF statusdefs.size %] - Status:
- [% FOREACH def = statusdefs %] - -
- [% END %] + [% IF flag_types.size > 0 %] + Flags:
+ [% PROCESS "flag/list.html.tmpl" bug_id=bugid attach_id=attachid %]
[% END %] - +
Comment (on the bug):

diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index e7aa8b0ef..59f749695 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -19,13 +19,18 @@ # Contributor(s): Myk Melez #%] +[%# Whether or not to include flags. %] +[% display_flags = num_attachment_flag_types > 0 %] +
- + [% IF display_flags %] + + [% END %] [% canseeprivate = !Param("insidergroup") || UserInGroup(Param("insidergroup")) %] @@ -50,16 +55,24 @@ - - + + [% END %] + - -[%# *** QAContact URL Summary Whiteboard Keywords *** %] +[%# *** QAContact URL Requests Summary Whiteboard Keywords *** %] [% IF Param('useqacontact') %] @@ -218,17 +218,23 @@ [% END %] - + - @@ -239,7 +245,7 @@ - @@ -252,7 +258,7 @@ Keywords: - @@ -263,8 +269,8 @@ [%# *** Attachments *** %] [% PROCESS attachment/list.html.tmpl - attachments = bug.attachments - bugid = bug.bug_id %] + attachments = bug.attachments + bugid = bug.bug_id %] [%# *** Dependencies Votes *** %] diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl new file mode 100644 index 000000000..951f248db --- /dev/null +++ b/template/en/default/flag/list.html.tmpl @@ -0,0 +1,94 @@ +[%# 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): Myk Melez + #%] + +
Attachment Type CreatedStatusFlagsActions
[% attachment.date %] - [% IF attachment.statuses.size == 0 %] - none - [% ELSE %] - [% FOREACH s = attachment.statuses %] - [% s FILTER html FILTER replace('\s', ' ') %]
+ [% IF display_flags %] +
+ [% IF attachment.flags.size == 0 %] + none + [% ELSE %] + [% FOR flag = attachment.flags %] + [% IF flag.setter %] + [% flag.setter.nick FILTER html %]: + [% END %] + [%+ flag.type.name %][% flag.status %] + [%+ IF flag.status == "?" && flag.requestee %] + ([% flag.requestee.nick %]) + [% END %]
+ [% END %] [% END %] - [% END %] -
[% IF attachment.canedit %] Edit @@ -72,7 +85,7 @@ [% END %]
+ Create a New Attachment (proposed patch, testcase, etc.) diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index 9cf33b8b5..ef9ec2d7f 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -194,7 +194,7 @@ [% END %]
+ + [% IF flag_types.size > 0 %] + Flags:
+ [% PROCESS "flag/list.html.tmpl" %] + [% END %] +
Summary: + Status Whiteboard: + +
+ + [% FOREACH type = flag_types %] + [% FOREACH flag = type.flags %] + + + + + + + [% END %] + [% IF !type.flags || type.flags.size == 0 %] + + + + + + + [% END %] + [% END %] + + [% FOREACH type = flag_types %] + [% NEXT UNLESS type.flags.size > 0 && type.is_multiplicable %] + [% IF !separator_displayed %] + + [% separator_displayed = 1 %] + [% END %] + + + + + + [% END %] + +
+ [% flag.setter.nick FILTER html %]: + + [% type.name FILTER html %] + + + + [% IF flag.status == "?" && flag.requestee %]([% flag.requestee.nick FILTER html %])[% END %] +
 [% type.name %] + + + [% IF type.is_requestable && type.is_requesteeble %] + () + [% END %] +

addl. [% type.name %] + + + [% IF type.is_requestable && type.is_requesteeble %] + () + [% END %] +
diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index bf93977ad..1981364f1 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -40,6 +40,10 @@ to any [% parameters %] which you may have set before calling ThrowCodeError. + [% ELSIF error == "action_unrecognized" %] + I don't recognize the value ([% variables.action FILTER html %]) + of the action variable. + [% ELSIF error == "attachment_already_obsolete" %] Attachment #[% attachid FILTER html %] ([% description FILTER html %]) is already obsolete. @@ -78,10 +82,40 @@ [% ELSIF error == "no_bug_data" %] No data when fetching bug [% bug_id %]. + [% ELSIF error == "flag_nonexistent" %] + There is no flag with ID #[% variables.id %]. + + [% ELSIF error == "flag_status_invalid" %] + The flag status [% variables.status FILTER html %] is invalid. + + [% ELSIF error == "flag_type_component_nonexistent" %] + The component [% variables.component FILTER html %] does not exist + in the product [% variables.product FILTER html %]. + + [% ELSIF error == "flag_type_component_without_product" %] + A component was selected without a product being selected. + + [% ELSIF error == "flag_type_id_invalid" %] + The flag type ID [% variables.id FILTER html %] is not + a positive integer. + + [% ELSIF error == "flag_type_nonexistent" %] + There is no flag type with the ID [% variables.id %]. + + [% ELSIF error == "flag_type_product_nonexistent" %] + The product [% variables.product FILTER html %] does not exist. + + [% ELSIF error == "flag_type_target_type_invalid" %] + The target type was neither bug nor attachment + but rather [% variables.target_type FILTER html %]. + [% ELSIF error == "no_y_axis_defined" %] No Y axis was defined when creating report. The X axis is optional, but the Y axis is compulsory. + [% ELSIF error == "request_queue_group_invalid" %] + The group field [% group FILTER html %] is invalid. + [% ELSIF error == "template_error" %] [% template_error_msg %] @@ -91,6 +125,14 @@ [% ELSIF error == "unknown_action" %] Unknown action [% action FILTER html %]! + [% ELSIF error == "unknown_component" %] + [% title = "Unknown Component" %] + There is no component named [% variables.component FILTER html %]. + + [% ELSIF error == "unknown_product" %] + [% title = "Unknown Product" %] + There is no product named [% variables.product FILTER html %]. + [% ELSE %] [%# Give sensible error if error functions are used incorrectly. #%] diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index 584c4a93e..85c678fdc 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -81,6 +81,34 @@ [% title = "Password Changed" %] Your password has been changed. + [% ELSIF message_tag == "flag_type_created" %] + [% title = "Flag Type Created" %] + The flag type [% name FILTER html %] has been created. + Back to flag types. + + [% ELSIF message_tag == "flag_type_changes_saved" %] + [% title = "Flag Type Changes Saved" %] +

+ Your changes to the flag type [% name FILTER html %] + have been saved. + Back to flag types. +

+ + [% ELSIF message_tag == "flag_type_deleted" %] + [% title = "Flag Type Deleted" %] +

+ The flag type [% name FILTER html %] has been deleted. + Back to flag types. +

+ + [% ELSIF message_tag == "flag_type_deactivated" %] + [% title = "Flag Type Deactivated" %] +

+ The flag type [% flag_type.name FILTER html %] + has been deactivated. + Back to flag types. +

+ [% ELSIF message_tag == "shutdown" %] [% title = "Bugzilla is Down" %] [% Param("shutdownhtml") %] diff --git a/template/en/default/global/select-menu.html.tmpl b/template/en/default/global/select-menu.html.tmpl index c27f60e8b..7b7fddb29 100644 --- a/template/en/default/global/select-menu.html.tmpl +++ b/template/en/default/global/select-menu.html.tmpl @@ -22,12 +22,18 @@ [%# INTERFACE: # name: string; the name of the menu. # + # multiple: boolean; whether or not the menu is multi-select + # + # size: integer; if multi-select, the number of items to display at once + # # options: array or hash; the items with which to populate the array. # If a hash is passed, the hash keys become the names displayed # to the user while the hash values become the value of the item. # # default: string; the item selected in the menu by default. # + # onchange: code; JavaScript to be run when the user changes the value + # selected in the menu. #%] [%# Get the scalar representation of the options reference, @@ -37,7 +43,9 @@ #%] [% options_type = BLOCK %][% options %][% END %] - [% IF options_type.search("ARRAY") %] [% FOREACH value = options %]
[% PROCESS "display_$column" %]
+[% END %] + +

Filter the Queue

+ +
+ + + + + + + + + + + + [%# We could let people see a "queue" of non-pending requests. %] + + + + + + + + + + + + +
Requester:Product: + + Flag: + [% PROCESS "global/select-menu.html.tmpl" + name="type" + options=types + default=form.type %] +
Requestee:Component: + + Group By: + [% groups = { + "Requester" => 'requester' , + "Requestee" => 'requestee', + "Flag" => 'type' , + "Product/Component" => 'category' + } %] + [% PROCESS "global/select-menu.html.tmpl" name="group" options=groups default=form.group %] +
+ +
+ +[% PROCESS global/footer.html.tmpl %] + +[% BLOCK start_new_table %] + [% "
" UNLESS group_value == "" %] +

[% column_headers.$group_field %]: [% request.$group_field FILTER html %]

+ + + [% FOREACH column = display_columns %] + [% NEXT IF column == group_field || excluded_columns.contains(column) %] + + [% END %] + + [% group_value = request.$group_field %] +[% END %] + +[% BLOCK display_type %] + [% request.type FILTER html %] +[% END %] + +[% BLOCK display_status %] + [% request.status %] +[% END %] + +[% BLOCK display_bug %] + + [% request.bug_id %]: [%+ request.bug_summary FILTER html %] +[% END %] + +[% BLOCK display_attachment %] + [% IF request.attach_id %] + + [% request.attach_id %]: [%+ request.attach_summary FILTER html %] + [% ELSE %] + N/A + [% END %] +[% END %] + +[% BLOCK display_requestee %] + [% request.requestee FILTER html %] +[% END %] + +[% BLOCK display_requester %] + [% request.requester FILTER html %] +[% END %] + +[% BLOCK display_created %] + [% request.created FILTER html %] +[% END %] + diff --git a/template/en/default/request/verify.html.tmpl b/template/en/default/request/verify.html.tmpl new file mode 100644 index 000000000..ad4c07d2c --- /dev/null +++ b/template/en/default/request/verify.html.tmpl @@ -0,0 +1,108 @@ + +[%# 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): Myk Melez + #%] + +[%# INTERFACE: + # form, mform: hashes; the form values submitted to the script, used by + # hidden-fields to generate hidden form fields replicating + # the original form + # flags: array; the flags the user made, including information about + # potential requestees for those flags (based on + # the string the user typed into the requestee fields) + # target: record; the bug/attachment for which the flags are being made + #%] + +[% UNLESS header_done %] + [% title = BLOCK %] + Verify Requests for Bug #[% target.bug.id %] + [% IF target.attachment %], Attachment #[% target.attachment.id %][% END %] + [% END %] + + [% h1 = BLOCK %] + Verify Requests for Bug #[% target.bug.id %] + [% IF target.attachment.exists %], + Attachment #[% target.attachment.id %] + [% END %] + [% END %] + + [% h2 = BLOCK %] + [% target.bug.summary FILTER html %] + [% IF target.attachment.exists %] + : [% target.attachment.summary FILTER html %] + [% END %] + [% END %] + + [% PROCESS global/header.html.tmpl %] +[% END %] + +
+ +[% PROCESS "global/hidden-fields.html.tmpl" + exclude=("^(flag_type|requestee)-") %] + +[% FOREACH flag = flags %] + [% IF flag.requestees.size == 0 %] +

+ Sorry, I can't find a user whose name or email address contains + the string [% flag.requestee_str FILTER html %]. + Double-check that the user's name or email address contains that + string, or try entering a shorter string. +

+

+ Ask + for [% flag.type.name FILTER html %] + +

+ + [% ELSIF flag.requestees.size == 1 %] + + + + [% ELSE %] +

+ More than one user's name or email address contains the string + [% flag.requestee_str FILTER html %]. Choose the user + you meant from the following menu or click the back button and try + again with a more specific string. +

+

+ Ask + for [% flag.type.name %] + +

+ + [% END %] +[% END %] + + + + + +[% PROCESS global/footer.html.tmpl %] + diff --git a/userprefs.cgi b/userprefs.cgi index 369c681ca..1d4be2a78 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -207,6 +207,11 @@ sub DoEmail { $vars->{'excludeself'} = 0; } + foreach my $flag qw(FlagRequestee FlagRequester) { + $vars->{$flag} = + !exists($emailflags{$flag}) || $emailflags{$flag} eq 'on'; + } + # Parse the info into a hash of hashes; the first hash keyed by role, # the second by reason, and the value being 1 or 0 for (on or off). # Preferences not existing in the user's list are assumed to be on. @@ -234,6 +239,10 @@ sub SaveEmail { $updateString .= 'ExcludeSelf~'; } + foreach my $flag qw(FlagRequestee FlagRequester) { + $updateString .= "~$flag~" . (defined($::FORM{$flag}) ? "on" : ""); + } + foreach my $role (@roles) { foreach my $reason (@reasons) { # Add this preference to the list without giving it a value, -- cgit v1.2.3-65-gdbad From 818ce46d9780c7a04ac04a3f116021f1edadd476 Mon Sep 17 00:00:00 2001 From: "bbaetz%student.usyd.edu.au" <> Date: Sat, 26 Oct 2002 08:56:55 +0000 Subject: Bug 147833 - start using CGI.pm r=gerv, justdave --- Bugzilla/CGI.pm | 149 +++++++++++++++ Bugzilla/Search.pm | 110 ++++++----- Bugzilla/Util.pm | 14 +- CGI.pl | 203 ++------------------- attachment.cgi | 65 ++++--- buglist.cgi | 29 +-- checksetup.pl | 11 +- globals.pl | 2 +- process_bug.cgi | 6 +- report.cgi | 18 +- template/en/default/global/code-error.html.tmpl | 7 +- template/en/default/reports/report-table.html.tmpl | 13 +- 12 files changed, 327 insertions(+), 300 deletions(-) create mode 100644 Bugzilla/CGI.pm (limited to 'attachment.cgi') diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm new file mode 100644 index 000000000..6a9730bc6 --- /dev/null +++ b/Bugzilla/CGI.pm @@ -0,0 +1,149 @@ +# -*- 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): Bradley Baetz + +use strict; + +package Bugzilla::CGI; + +use CGI qw(-no_xhtml -oldstyle_urls :private_tempfiles); + +use base qw(CGI); + +use Bugzilla::Util; + +# CGI.pm uses AUTOLOAD, but explicitly defines a DESTROY sub. +# We need to do so, too, otherwise perl dies when the object is destroyed +# and we don't have a DESTROY method (because CGI.pm's AUTOLOAD will |die| +# on getting an unknown sub to try to call) +sub DESTROY {}; + +sub new { + my ($invocant, @args) = @_; + my $class = ref($invocant) || $invocant; + + my $self = $class->SUPER::new(@args); + + # Check for errors + # All of the Bugzilla code wants to do this, so do it here instead of + # in each script + + my $err = $self->cgi_error; + + if ($err) { + # XXX - under mod_perl we can use the request object to + # enable the apache ErrorDocument stuff, which is localisable + # (and localised by default under apache2). + # This doesn't appear to be possible under mod_cgi. + # Under mod_perl v2, though, this happens automatically, and the + # message body is ignored. + + # Note that this error block is only triggered by CGI.pm for malformed + # multipart requests, and so should never happen unless there is a + # browser bug. + + # Using CGI.pm to do this means that ThrowCodeError prints the + # content-type again... + #print $self->header(-status => $err); + print "Status: $err\n"; + + my $vars = {}; + if ($err =~ m/(\d{3})\s(.*)/) { + $vars->{http_error_code} = $1; + $vars->{http_error_string} = $2; + } else { + $vars->{http_error_string} = $err; + } + + &::ThrowCodeError("cgi_error", $vars); + } + + return $self; +} + +# We want this sorted plus the ability to exclude certain params +sub canonicalise_query { + my ($self, @exclude) = @_; + + # Reconstruct the URL by concatenating the sorted param=value pairs + my @parameters; + foreach my $key (sort($self->param())) { + # Leave this key out if it's in the exclude list + next if lsearch(\@exclude, $key) != -1; + + my $esc_key = url_quote($key); + + foreach my $value ($self->param($key)) { + if ($value) { + my $esc_value = url_quote($value); + + push(@parameters, "$esc_key=$esc_value"); + } + } + } + + return join("&", @parameters); +} + +1; + +__END__ + +=head1 NAME + + Bugzilla::CGI - CGI handling for Bugzilla + +=head1 SYNOPSIS + + use Bugzilla::CGI; + + my $cgi = new Bugzilla::CGI(); + +=head1 DESCRIPTION + +This package inherits from the standard CGI module, to provide additional +Bugzilla-specific functionality. In general, see L for +documention. + +=head1 CHANGES FROM L + +Bugzilla::CGI has some differences from L. + +=over 4 + +=item C is automatically checked + +After creating the CGI object, C automatically checks +I, and throws a CodeError if a problem is detected. + +=back + +=head1 ADDITIONAL FUNCTIONS + +I also includes additional functions. + +=over 4 + +=item C + +This returns a sorted string of the paramaters, suitable for use in a url. +Values in C<@exclude> are not included in the result. + +=back diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 36311d6c4..9376a09fc 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -40,6 +40,7 @@ use Date::Format; use Date::Parse; # Create a new Search +# Note that the param argument may be modified by Bugzilla::Search sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; @@ -55,7 +56,7 @@ sub new { sub init { my $self = shift; my $fieldsref = $self->{'fields'}; - my $urlstr = $self->{'url'}; + my $params = $self->{'params'}; my $debug = 0; @@ -64,9 +65,6 @@ sub init { my @wherepart; my @having = ("(cntuseringroups = cntbugingroups OR canseeanyway)"); @fields = @$fieldsref if $fieldsref; - my %F; - my %M; - &::ParseUrlString($urlstr, \%F, \%M); my @specialchart; my @andlist; @@ -96,8 +94,8 @@ sub init { } my $minvotes; - if (defined $F{'votes'}) { - my $c = trim($F{'votes'}); + if (defined $params->param('votes')) { + my $c = trim($params->param('votes')); if ($c ne "") { if ($c !~ /^[0-9]*$/) { $::vars->{'value'} = $c; @@ -107,12 +105,12 @@ sub init { } } - if ($M{'bug_id'}) { + if ($params->param('bug_id')) { my $type = "anyexact"; - if ($F{'bugidtype'} && $F{'bugidtype'} eq 'exclude') { + if ($params->param('bugidtype') && $params->param('bugidtype') eq 'exclude') { $type = "nowords"; } - push(@specialchart, ["bug_id", $type, join(',', @{$M{'bug_id'}})]); + push(@specialchart, ["bug_id", $type, join(',', $params->param('bug_id'))]); } my @legal_fields = ("product", "version", "rep_platform", "op_sys", @@ -120,33 +118,33 @@ sub init { "assigned_to", "reporter", "component", "target_milestone", "bug_group"); - foreach my $field (keys %F) { + foreach my $field ($params->param()) { if (lsearch(\@legal_fields, $field) != -1) { push(@specialchart, [$field, "anyexact", - join(',', @{$M{$field}})]); + join(',', $params->param($field))]); } } - if ($F{'product'}) { + if ($params->param('product')) { push(@supptables, "products products_"); push(@wherepart, "products_.id = bugs.product_id"); push(@specialchart, ["products_.name", "anyexact", - join(',',@{$M{'product'}})]); + join(',',$params->param('product'))]); } - if ($F{'component'}) { + if ($params->param('component')) { push(@supptables, "components components_"); push(@wherepart, "components_.id = bugs.component_id"); push(@specialchart, ["components_.name", "anyexact", - join(',',@{$M{'component'}})]); + join(',',$params->param('component'))]); } - if ($F{'keywords'}) { - my $t = $F{'keywords_type'}; + if ($params->param('keywords')) { + my $t = $params->param('keywords_type'); if (!$t || $t eq "or") { $t = "anywords"; } - push(@specialchart, ["keywords", $t, $F{'keywords'}]); + push(@specialchart, ["keywords", $t, $params->param('keywords')]); } if (lsearch($fieldsref, "(SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id)) AS actual_time") != -1) { @@ -155,14 +153,14 @@ sub init { } foreach my $id ("1", "2") { - if (!defined ($F{"email$id"})) { + if (!defined ($params->param("email$id"))) { next; } - my $email = trim($F{"email$id"}); + my $email = trim($params->param("email$id")); if ($email eq "") { next; } - my $type = $F{"emailtype$id"}; + my $type = $params->param("emailtype$id"); if ($type eq "exact") { $type = "anyexact"; foreach my $name (split(',', $email)) { @@ -175,11 +173,11 @@ sub init { my @clist; foreach my $field ("assigned_to", "reporter", "cc", "qa_contact") { - if ($F{"email$field$id"}) { + if ($params->param("email$field$id")) { push(@clist, $field, $type, $email); } } - if ($F{"emaillongdesc$id"}) { + if ($params->param("emaillongdesc$id")) { my $table = "longdescs_"; push(@supptables, "longdescs $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); @@ -197,8 +195,8 @@ sub init { } - if (defined $F{'changedin'}) { - my $c = trim($F{'changedin'}); + if (defined $params->param('changedin')) { + my $c = trim($params->param('changedin')); if ($c ne "") { if ($c !~ /^[0-9]*$/) { $::vars->{'value'} = $c; @@ -209,15 +207,15 @@ sub init { } } - my $ref = $M{'chfield'}; + my @chfield = $params->param('chfield'); - if (defined $ref) { - my $which = lsearch($ref, "[Bug creation]"); + if (@chfield) { + my $which = lsearch(\@chfield, "[Bug creation]"); if ($which >= 0) { - splice(@$ref, $which, 1); + splice(@chfield, $which, 1); push(@specialchart, ["creation_ts", "greaterthan", - SqlifyDate($F{'chfieldfrom'})]); - my $to = $F{'chfieldto'}; + SqlifyDate($params->param('chfieldfrom'))]); + my $to = $params->param('chfieldto'); if (defined $to) { $to = trim($to); if ($to ne "" && $to !~ /^now$/i) { @@ -228,18 +226,18 @@ sub init { } } - if (defined $ref && 0 < @$ref) { + if (@chfield) { push(@supptables, "bugs_activity actcheck"); my @list; - foreach my $f (@$ref) { + foreach my $f (@chfield) { push(@list, "\nactcheck.fieldid = " . &::GetFieldID($f)); } push(@wherepart, "actcheck.bug_id = bugs.bug_id"); push(@wherepart, "(" . join(' OR ', @list) . ")"); push(@wherepart, "actcheck.bug_when >= " . - &::SqlQuote(SqlifyDate($F{'chfieldfrom'}))); - my $to = $F{'chfieldto'}; + &::SqlQuote(SqlifyDate($params->param('chfieldfrom')))); + my $to = $params->param('chfieldto'); if (defined $to) { $to = trim($to); if ($to ne "" && $to !~ /^now$/i) { @@ -247,7 +245,7 @@ sub init { &::SqlQuote(SqlifyDate($to))); } } - my $value = $F{'chfieldvalue'}; + my $value = $params->param('chfieldvalue'); if (defined $value) { $value = trim($value); if ($value ne "") { @@ -259,12 +257,12 @@ sub init { foreach my $f ("short_desc", "long_desc", "bug_file_loc", "status_whiteboard") { - if (defined $F{$f}) { - my $s = trim($F{$f}); + if (defined $params->param($f)) { + my $s = trim($params->param($f)); if ($s ne "") { my $n = $f; my $q = &::SqlQuote($s); - my $type = $F{$f . "_type"}; + my $type = $params->param($f . "_type"); push(@specialchart, [$f, $type, $s]); } } @@ -516,7 +514,7 @@ sub init { if ($t eq "anywords") { $term = $haveawordterm; } elsif ($t eq "allwords") { - $ref = $funcsbykey{",$t"}; + my $ref = $funcsbykey{",$t"}; &$ref; if ($term && $haveawordterm) { $term = "(($term) AND $haveawordterm)"; @@ -533,7 +531,7 @@ sub init { my $table = "dependson_" . $chartid; push(@supptables, "dependencies $table"); $ff = "$table.$f"; - $ref = $funcsbykey{",$t"}; + my $ref = $funcsbykey{",$t"}; &$ref; push(@wherepart, "$table.blocked = bugs.bug_id"); }, @@ -542,7 +540,7 @@ sub init { my $table = "blocked_" . $chartid; push(@supptables, "dependencies $table"); $ff = "$table.$f"; - $ref = $funcsbykey{",$t"}; + my $ref = $funcsbykey{",$t"}; &$ref; push(@wherepart, "$table.dependson = bugs.bug_id"); }, @@ -672,9 +670,9 @@ sub init { # first we delete any sign of "Chart #-1" from the HTML form hash # since we want to guarantee the user didn't hide something here - my @badcharts = grep /^(field|type|value)-1-/, (keys %F); + my @badcharts = grep /^(field|type|value)-1-/, $params->param(); foreach my $field (@badcharts) { - delete $F{$field}; + $params->delete($field); } # now we take our special chart and stuff it into the form hash @@ -683,11 +681,11 @@ sub init { foreach my $ref (@specialchart) { my $col = 0; while (@$ref) { - $F{"field$chart-$row-$col"} = shift(@$ref); - $F{"type$chart-$row-$col"} = shift(@$ref); - $F{"value$chart-$row-$col"} = shift(@$ref); + $params->param("field$chart-$row-$col", shift(@$ref)); + $params->param("type$chart-$row-$col", shift(@$ref)); + $params->param("value$chart-$row-$col", shift(@$ref)); if ($debug) { - print qq{

$F{"field$chart-$row-$col"} | $F{"type$chart-$row-$col"} | $F{"value$chart-$row-$col"}*

\n}; + print qq{

$params->param("field$chart-$row-$col") | $params->param("type$chart-$row-$col") | $params->param("value$chart-$row-$col")*

\n}; } $col++; @@ -786,19 +784,19 @@ sub init { $row = 0; for ($chart=-1 ; - $chart < 0 || exists $F{"field$chart-0-0"} ; + $chart < 0 || $params->param("field$chart-0-0") ; $chart++) { $chartid = $chart >= 0 ? $chart : ""; for ($row = 0 ; - exists $F{"field$chart-$row-0"} ; + $params->param("field$chart-$row-0") ; $row++) { my @orlist; for (my $col = 0 ; - exists $F{"field$chart-$row-$col"} ; + $params->param("field$chart-$row-$col") ; $col++) { - $f = $F{"field$chart-$row-$col"} || "noop"; - $t = $F{"type$chart-$row-$col"} || "noop"; - $v = $F{"value$chart-$row-$col"}; + $f = $params->param("field$chart-$row-$col") || "noop"; + $t = $params->param("type$chart-$row-$col") || "noop"; + $v = $params->param("value$chart-$row-$col"); $v = "" if !defined $v; $v = trim($v); if ($f eq "noop" || $t eq "noop" || $v eq "") { @@ -841,8 +839,8 @@ sub init { } else { # This field and this type don't work together. - $::vars->{'field'} = $F{"field$chart-$row-$col"}; - $::vars->{'type'} = $F{"type$chart-$row-$col"}; + $::vars->{'field'} = $params->param("field$chart-$row-$col"); + $::vars->{'type'} = $params->param("type$chart-$row-$col"); &::ThrowCodeError("field_type_mismatch"); } } diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index 4d1fc3aa6..f87c6fbc6 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -27,7 +27,7 @@ package Bugzilla::Util; use base qw(Exporter); @Bugzilla::Util::EXPORT = qw(is_tainted trick_taint detaint_natural - html_quote value_quote + html_quote url_quote value_quote lsearch max min trim); @@ -64,6 +64,13 @@ sub html_quote { return $var; } +# This orignally came from CGI.pm, by Lincoln D. Stein +sub url_quote { + my ($toencode) = (@_); + $toencode =~ s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg; + return $toencode; +} + sub value_quote { my ($var) = (@_); $var =~ s/\&/\&/g; @@ -134,6 +141,7 @@ Bugzilla::Util - Generic utility functions for bugzilla # Functions for quoting html_quote($var); + url_quote($var); value_quote($var); # Functions for searching @@ -200,6 +208,10 @@ be done in the template where possible. Returns a value quoted for use in HTML, with &, E, E, and E<34> being replaced with their appropriate HTML entities. +=item C + +Quotes characters so that they may be included as part of a url. + =item C As well as escaping html like C, this routine converts newlines diff --git a/CGI.pl b/CGI.pl index 6ca5f2588..d6dca3a39 100644 --- a/CGI.pl +++ b/CGI.pl @@ -46,7 +46,6 @@ use Bugzilla::Config; sub CGI_pl_sillyness { my $zz; - $zz = %::MFORM; $zz = %::dontchange; } @@ -83,151 +82,6 @@ sub url_decode { return $todecode; } -# Quotify a string, suitable for putting into a URL. -sub url_quote { - my($toencode) = (@_); - $toencode=~s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg; - return $toencode; -} - -sub ParseUrlString { - my ($buffer, $f, $m) = (@_); - undef %$f; - undef %$m; - - my %isnull; - - # We must make sure that the CGI params remain tainted. - # This means that if for some reason you want to make this code - # use a regexp and $1, $2, ... (or use a helper function which does so) - # you must |use re 'taint'| _and_ make sure that you don't run into - # http://bugs.perl.org/perlbug.cgi?req=bug_id&bug_id=20020704.001 - my @args = split('&', $buffer); - foreach my $arg (@args) { - my ($name, $value) = split('=', $arg, 2); - $value = '' if not defined $value; - - $name = url_decode($name); - $value = url_decode($value); - - if ($value ne "") { - if (defined $f->{$name}) { - $f->{$name} .= $value; - my $ref = $m->{$name}; - push @$ref, $value; - } else { - $f->{$name} = $value; - $m->{$name} = [$value]; - } - } else { - $isnull{$name} = 1; - } - } - if (%isnull) { - foreach my $name (keys(%isnull)) { - if (!defined $f->{$name}) { - $f->{$name} = ""; - $m->{$name} = []; - } - } - } -} - -sub ProcessFormFields { - my ($buffer) = (@_); - return ParseUrlString($buffer, \%::FORM, \%::MFORM); -} - -sub ProcessMultipartFormFields { - my ($boundary) = @_; - - # Initialize variables that store whether or not we are parsing a header, - # the name of the part we are parsing, and its value (which is incomplete - # until we finish parsing the part). - my $inheader = 1; - my $fieldname = ""; - my $fieldvalue = ""; - - # Read the input stream line by line and parse it into a series of parts, - # each one containing a single form field and its value and each one - # separated from the next by the value of $boundary. - my $remaining = $ENV{"CONTENT_LENGTH"}; - while ($remaining > 0 && ($_ = )) { - $remaining -= length($_); - - # If the current input line is a boundary line, save the previous - # form value and reset the storage variables. - if ($_ =~ m/^-*\Q$boundary\E/) { - if ( $fieldname ) { - chomp($fieldvalue); - $fieldvalue =~ s/\r$//; - if ( defined $::FORM{$fieldname} ) { - $::FORM{$fieldname} .= $fieldvalue; - push @{$::MFORM{$fieldname}}, $fieldvalue; - } else { - $::FORM{$fieldname} = $fieldvalue; - $::MFORM{$fieldname} = [$fieldvalue]; - } - } - - $inheader = 1; - $fieldname = ""; - $fieldvalue = ""; - - # If the current input line is a header line, look for a blank line - # (meaning the end of the headers), a Content-Disposition header - # (containing the field name and, for uploaded file parts, the file - # name), or a Content-Type header (containing the content type for - # file parts). - } elsif ( $inheader ) { - if (m/^\s*$/) { - $inheader = 0; - } elsif (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) { - $fieldname = $1; - if (m/;\s*filename\s*=\s*"([^\"]+)"/i) { - $::FILE{$fieldname}->{'filename'} = $1; - } - } elsif ( m|^Content-Type:\s*([^/]+/[^\s;]+)|i ) { - $::FILE{$fieldname}->{'contenttype'} = $1; - } - - # If the current input line is neither a boundary line nor a header, - # it must be part of the field value, so append it to the value. - } else { - $fieldvalue .= $_; - } - } -} - -sub CanonicaliseParams { - my ($buffer, $exclude) = (@_); - my %pieces; - - # Split the buffer up into key/value pairs, and store the non-empty ones - my @args = split('&', $buffer); - - foreach my $arg (@args) { - my ($name, $value) = split('=', $arg, 2); - - if ($value) { - push(@{$pieces{$name}}, $value); - } - } - - # Reconstruct the URL by concatenating the sorted param=value pairs - my @parameters; - foreach my $key (sort keys %pieces) { - # Leave this key out if it's in the exclude list - next if lsearch($exclude, $key) != -1; - - foreach my $value (@{$pieces{$key}}) { - push(@parameters, "$key=$value"); - } - } - - return join("&", @parameters); -} - # check and see if a given field exists, is non-empty, and is set to a # legal value. assume a browser bug and abort appropriately if not. # if $legalsRef is not passed, just check to make sure the value exists and @@ -1020,52 +874,31 @@ sub GetBugActivity { return(\@operations, $incomplete_data); } - ############# Live code below here (that is, not subroutine defs) ############# -$| = 1; +use Bugzilla::CGI(); -# Uncommenting this next line can help debugging. -# print "Content-type: text/html\n\nHello mom\n"; +# XXX - mod_perl, this needs to move into all the scripts individually +# Once we do that, look into setting DISABLE_UPLOADS, and overriding +# on a per-script basis +$::cgi = new Bugzilla::CGI(); -# foreach my $k (sort(keys %ENV)) { -# print "$k $ENV{$k}
\n"; -# } +# Set up stuff for compatibility with the old CGI.pl code +# This code will be removed as soon as possible, in favour of +# using the CGI.pm stuff directly -if (defined $ENV{"REQUEST_METHOD"}) { - if ($ENV{"REQUEST_METHOD"} eq "GET") { - if (defined $ENV{"QUERY_STRING"}) { - $::buffer = $ENV{"QUERY_STRING"}; - } else { - $::buffer = ""; - } - ProcessFormFields $::buffer; - } else { - if (exists($ENV{"CONTENT_TYPE"}) && $ENV{"CONTENT_TYPE"} =~ - m@multipart/form-data; boundary=\s*([^; ]+)@) { - ProcessMultipartFormFields($1); - $::buffer = ""; - } else { - read STDIN, $::buffer, $ENV{"CONTENT_LENGTH"} || - die "Couldn't get form data"; - ProcessFormFields $::buffer; - } - } +# XXX - mod_perl - reset these between runs + +foreach my $name ($::cgi->param()) { + my @val = $::cgi->param($name); + $::FORM{$name} = join('', @val); + $::MFORM{$name} = \@val; } -if (defined $ENV{"HTTP_COOKIE"}) { - # Don't trust anything which came in as a cookie - use re 'taint'; - foreach my $pair (split(/;/, $ENV{"HTTP_COOKIE"})) { - $pair = trim($pair); - if ($pair =~ /^([^=]*)=(.*)$/) { - if (!exists($::COOKIE{$1})) { - $::COOKIE{$1} = $2; - } - } else { - $::COOKIE{$pair} = ""; - } - } +$::buffer = $::cgi->query_string(); + +foreach my $name ($::cgi->cookie()) { + $::COOKIE{$name} = $::cgi->cookie($name); } 1; diff --git a/attachment.cgi b/attachment.cgi index b185312c6..6e9379af1 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -33,16 +33,11 @@ use strict; use lib qw(.); use vars qw( + $cgi $template $vars ); -# Win32 specific hack to avoid a hang when creating/showing an attachment -if ($^O eq 'MSWin32') { - binmode(STDIN); - binmode(STDOUT); -} - # Include the Bugzilla CGI and general utility library. require "CGI.pl"; @@ -89,12 +84,12 @@ elsif ($action eq "insert") ValidateBugID($::FORM{'bugid'}); ValidateComment($::FORM{'comment'}); validateFilename(); - validateData(); - validateDescription(); validateIsPatch(); + my $data = validateData(); + validateDescription(); validateContentType() unless $::FORM{'ispatch'}; validateObsolete() if $::FORM{'obsolete'}; - insert(); + insert($data); } elsif ($action eq "edit") { @@ -198,13 +193,14 @@ sub validateContentType } elsif ($::FORM{'contenttypemethod'} eq 'autodetect') { + my $contenttype = $cgi->uploadInfo($cgi->param('data'))->{'Content-Type'}; # The user asked us to auto-detect the content type, so use the type # specified in the HTTP request headers. - if ( !$::FILE{'data'}->{'contenttype'} ) + if ( !$contenttype ) { ThrowUserError("missing_content_type"); } - $::FORM{'contenttype'} = $::FILE{'data'}->{'contenttype'}; + $::FORM{'contenttype'} = $contenttype; } elsif ($::FORM{'contenttypemethod'} eq 'list') { @@ -247,29 +243,40 @@ sub validatePrivate sub validateData { - $::FORM{'data'} - || ThrowUserError("zero_length_file"); + my $maxsize = $::FORM{'ispatch'} ? Param('maxpatchsize') : Param('maxattachmentsize'); + $maxsize *= 1024; # Convert from K - my $len = length($::FORM{'data'}); + my $fh = $cgi->upload('data'); + my $data; - my $maxpatchsize = Param('maxpatchsize'); - my $maxattachmentsize = Param('maxattachmentsize'); - - # Makes sure the attachment does not exceed either the "maxpatchsize" or - # the "maxattachmentsize" parameter. - if ( $::FORM{'ispatch'} && $maxpatchsize && $len > $maxpatchsize*1024 ) + # We could get away with reading only as much as required, except that then + # we wouldn't have a size to print to the error handler below. { - $vars->{'filesize'} = sprintf("%.0f", $len/1024); - ThrowUserError("patch_too_large"); - } elsif ( !$::FORM{'ispatch'} && $maxattachmentsize && $len > $maxattachmentsize*1024 ) { - $vars->{'filesize'} = sprintf("%.0f", $len/1024); - ThrowUserError("file_too_large"); + # enable 'slurp' mode + local $/; + $data = <$fh>; } + + $data + || ThrowUserError("zero_length_file"); + + # Make sure the attachment does not exceed the maximum permitted size + my $len = length($data); + if ($maxsize && $len > $maxsize) { + $vars->{'filesize'} = sprintf("%.0f", $len/1024); + if ( $::FORM{'ispatch'} ) { + ThrowUserError("patch_too_large"); + } else { + ThrowUserError("file_too_large"); + } + } + + return $data; } sub validateFilename { - defined $::FILE{'data'} + defined $cgi->upload('data') || ThrowUserError("file_not_specified"); } @@ -428,13 +435,15 @@ sub enter sub insert { + my ($data) = @_; + # Insert a new attachment into the database. # Escape characters in strings that will be used in SQL statements. - my $filename = SqlQuote($::FILE{'data'}->{'filename'}); + my $filename = SqlQuote($cgi->param('data')); my $description = SqlQuote($::FORM{'description'}); my $contenttype = SqlQuote($::FORM{'contenttype'}); - my $thedata = SqlQuote($::FORM{'data'}); + my $thedata = SqlQuote($data); my $isprivate = $::FORM{'isprivate'} ? 1 : 0; # Insert the attachment into the database. diff --git a/buglist.cgi b/buglist.cgi index 74015bc2a..684b7dfe0 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -33,7 +33,7 @@ use strict; use lib qw(.); -use vars qw($template $vars); +use vars qw($cgi $template $vars); use Bugzilla::Search; @@ -229,13 +229,17 @@ if ($::FORM{'cmdtype'} eq "runnamed") { $::FORM{'remaction'} = "run"; } +# The params object to use for the actual query itsself +# This will be modified, so make a copy +my $params = new Bugzilla::CGI($cgi); + # Take appropriate action based on user's request. if ($::FORM{'cmdtype'} eq "dorem") { if ($::FORM{'remaction'} eq "run") { - $::buffer = LookupNamedQuery($::FORM{"namedcmd"}); + my $query = LookupNamedQuery($::FORM{"namedcmd"}); $vars->{'title'} = "Bug List: $::FORM{'namedcmd'}"; - ProcessFormFields($::buffer); - $order = $::FORM{'order'} || $order; + $params = new Bugzilla::CGI($query); + $order = $params->param('order') || $order; } elsif ($::FORM{'remaction'} eq "load") { my $url = "query.cgi?" . LookupNamedQuery($::FORM{"namedcmd"}); @@ -391,14 +395,14 @@ DefineColumn("percentage_complete","(100*((SUM(ldtime.work_time)*COUNT(DISTINCT # Determine the columns that will be displayed in the bug list via the # columnlist CGI parameter, the user's preferences, or the default. my @displaycolumns = (); -if (defined $::FORM{'columnlist'}) { - if ($::FORM{'columnlist'} eq "all") { +if (defined $params->param('columnlist')) { + if ($params->param('columnlist') eq "all") { # If the value of the CGI parameter is "all", display all columns, # but remove the redundant "summaryfull" column. @displaycolumns = grep($_ ne 'summaryfull', keys(%$columns)); } else { - @displaycolumns = split(/[ ,]+/, $::FORM{'columnlist'}); + @displaycolumns = split(/[ ,]+/, $params->param('columnlist')); } } elsif (defined $::COOKIE{'COLUMNLIST'}) { @@ -424,9 +428,10 @@ else { # number of votes and the votes column is not already on the list. # Some versions of perl will taint 'votes' if this is done as a single -# statement, because $::FORM{'votes'} is tainted at this point -$::FORM{'votes'} ||= ""; -if (trim($::FORM{'votes'}) && !grep($_ eq 'votes', @displaycolumns)) { +# statement, because the votes param is tainted at this point +my $votes = $params->param('votes'); +$votes ||= ""; +if (trim($votes) && !grep($_ eq 'votes', @displaycolumns)) { push(@displaycolumns, 'votes'); } @@ -479,7 +484,7 @@ my @selectnames = map($columns->{$_}->{'name'}, @selectcolumns); # Generate the basic SQL query that will be used to generate the bug list. my $search = new Bugzilla::Search('fields' => \@selectnames, - 'url' => $::buffer); + 'params' => $params); my $query = $search->getSQL(); @@ -489,7 +494,7 @@ my $query = $search->getSQL(); # Add to the query some instructions for sorting the bug list. if ($::COOKIE{'LASTORDER'} && (!$order || $order =~ /^reuse/i)) { - $order = url_decode($::COOKIE{'LASTORDER'}); + $order = $::COOKIE{'LASTORDER'}; $order_from_cookie = 1; } diff --git a/checksetup.pl b/checksetup.pl index 9f22ae1f5..1acec457c 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -179,6 +179,13 @@ sub have_vers { $vnum = ${"${pkg}::VERSION"} || ${"${pkg}::Version"} || 0; $vnum = -1 if $@; + # CGI's versioning scheme went 2.75, 2.751, 2.752, 2.753, 2.76 + # That breaks the standard version tests, so we need to manually correct + # the version + if ($pkg eq 'CGI' && $vnum =~ /(2\.7\d)(\d+)/) { + $vnum = $1 . "." . $2; + } + if ($vnum eq "-1") { # string compare just in case it's non-numeric $vstr = "not found"; } @@ -201,8 +208,8 @@ my $modules = [ version => '1.52' }, { - name => 'CGI::Carp', - version => '0' + name => 'CGI', + version => '2.88' }, { name => 'Data::Dumper', diff --git a/globals.pl b/globals.pl index da954181a..bee0ed9ff 100644 --- a/globals.pl +++ b/globals.pl @@ -1584,7 +1584,7 @@ $::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 => \&url_quote , + url_quote => \&Bugzilla::Util::url_quote, # In CSV, quotes are doubled, and any value containing a quote or a # comma is enclosed in quotes. diff --git a/process_bug.cgi b/process_bug.cgi index 54ed0dc8f..47f038e52 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -697,7 +697,11 @@ if (Param("usebugaliases") && defined($::FORM{'alias'})) { # with that value. DoComma(); $::query .= "alias = "; - $::query .= ($alias eq "") ? "NULL" : SqlQuote($alias); + if ($alias eq "") { + $::query .= "NULL"; + } else { + $::query .= SqlQuote($alias); + } } } diff --git a/report.cgi b/report.cgi index 9e60c1dc9..f4cb74dad 100755 --- a/report.cgi +++ b/report.cgi @@ -26,7 +26,7 @@ use lib "."; require "CGI.pl"; -use vars qw($template $vars); +use vars qw($cgi $template $vars); use Bugzilla::Search; @@ -77,11 +77,13 @@ my @axis_fields = ($row_field, $col_field, $tbl_field); my @selectnames = map($columns{$_}, @axis_fields); +# Clone the params, so that Bugzilla::Search can modify them +my $params = new Bugzilla::CGI($cgi); my $search = new Bugzilla::Search('fields' => \@selectnames, - 'url' => $::buffer); + 'params' => $params); my $query = $search->getSQL(); -SendSQL($query, $::userid); +SendSQL($query); # We have a hash of hashes for the data itself, and a hash to hold the # row/col/table names. @@ -108,12 +110,14 @@ $vars->{'names'} = \%names; $vars->{'data'} = \%data; $vars->{'time'} = time(); -$::buffer =~ s/format=[^&]*&?//g; +$cgi->delete('format'); # Calculate the base query URL for the hyperlinked numbers -$vars->{'buglistbase'} = CanonicaliseParams($::buffer, - ["x_axis_field", "y_axis_field", "z_axis_field", @axis_fields]); -$vars->{'buffer'} = $::buffer; +$vars->{'querybase'} = $cgi->canonicalise_query("x_axis_field", + "y_axis_field", + "z_axis_field", + @axis_fields); +$vars->{'query'} = $cgi->query_string(); # Generate and return the result from the appropriate template. my $format = GetFormat("reports/report", $::FORM{'format'}, $::FORM{'ctype'}); diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index 1ec1c4626..baad2f5f0 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -47,6 +47,11 @@ [% ELSIF error == "attachment_already_obsolete" %] Attachment #[% attachid FILTER html %] ([% description FILTER html %]) is already obsolete. + + [% ELSIF error == "cgi_error" %] + [% title = "CGI Error" %] + Bugzilla has had trouble interpreting your CGI request; + [%+ Param('browserbugmessage') %] [% ELSIF error == "chart_data_not_generated" %] The tool which gathers bug counts has not been run yet. @@ -236,7 +241,7 @@
 Variables:
   [% FOREACH key = variables.keys %]
-    [%+ key %]: [%+ variables.$key %]
+    [%+ key FILTER html %]: [%+ variables.$key FILTER html %]
   [% END %]
   
[% END %] diff --git a/template/en/default/reports/report-table.html.tmpl b/template/en/default/reports/report-table.html.tmpl index 97dae5b48..9767f5030 100644 --- a/template/en/default/reports/report-table.html.tmpl +++ b/template/en/default/reports/report-table.html.tmpl @@ -21,7 +21,8 @@ #%] [%# INTERFACE: - # basequery: The base query for this table, in URL form + # querybase: The base query for this table, in URL form + # query: The query for this table, in URL form # data: hash of hash of hash of numbers. Bug counts. # names: hash of hash of strings. Names of tables, rows and columns. # col_field: string. Name of the field being plotted as columns. @@ -149,7 +150,7 @@ [% col_idx = 1 - col_idx %]
[% END %] @@ -202,7 +203,7 @@ [% END %] - Edit this report + Edit this report
-- cgit v1.2.3-65-gdbad From 8f8766ab97ba1e4f55c0254e65131b5585c19a2e Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Fri, 8 Nov 2002 09:35:36 +0000 Subject: Fix for bug 172518: makes the request tracker use the generic user matching code r=not_erik,joel a=justdave --- Bugzilla/Flag.pm | 59 +---------- Bugzilla/User.pm | 36 ++++++- attachment.cgi | 2 + process_bug.cgi | 7 +- .../en/default/global/confirm-user-match.html.tmpl | 15 ++- template/en/default/request/verify.html.tmpl | 108 --------------------- 6 files changed, 52 insertions(+), 175 deletions(-) delete mode 100644 template/en/default/request/verify.html.tmpl (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 997b46bf7..7395be09d 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -368,10 +368,6 @@ sub clear { sub FormToNewFlags { my ($target, $data) = @_; - # Flag for whether or not we must get verification of the requestees - # (if the user did not uniquely identify them). - my $verify_requestees = 0; - # Get information about the setter to add to each flag. # Uses a conditional to suppress Perl's "used only once" warnings. my $setter = new Bugzilla::User($::userid); @@ -396,67 +392,20 @@ sub FormToNewFlags { my $requestee_str = $data->{"requestee-$type_id"} || $data->{'requestee'}; if ($requestee_str) { - $flag->{'requestee_str'} = $requestee_str; - MatchRequestees($flag); - $verify_requestees = 1 if scalar(@{$flag->{'requestees'}}) != 1; + my $requestee_id = &::DBname_to_id($requestee_str); + $requestee_id + || &::ThrowUserError("invalid_username", {name => $requestee_str}); + $flag->{'requestee'} = new Bugzilla::User($requestee_id); } # Add the flag to the array of flags. push(@flags, $flag); } - if ($verify_requestees) { - $::vars->{'target'} = $target; - $::vars->{'flags'} = \@flags; - $::vars->{'form'} = $data; - $::vars->{'mform'} = \%::MFORM || \%::MFORM; - - print "Content-Type: text/html\n\n" unless $::vars->{'header_done'}; - $::template->process("request/verify.html.tmpl", $::vars) - || &::ThrowTemplateError($::template->error()); - exit; - } - # Return the list of flags. return \@flags; } -sub MatchRequestees { - my ($flag) = @_; - - my $requestee_str = $flag->{'requestee_str'}; - - # To reduce the size of queries, require the user to enter at least - # three characters of each requestee's name unless this installation - # automatically appends an email suffix to each user's login name, - # in which case we can't guarantee their names are at least three - # characters long. - if (!Param('emailsuffix') && length($requestee_str) < 3) { - &::ThrowUserError("requestee_too_short"); - } - - # Get a list of potential requestees whose email address or real name - # matches the substring entered by the user. Try an exact match first, - # then fall back to a substring search. Limit search to 100 matches, - # since at that point there are too many to make the user wade through, - # and we need to get the user to enter a more constrictive match string. - my $user_id = &::DBname_to_id($requestee_str); - if ($user_id) { $flag->{'requestees'} = [ new Bugzilla::User($user_id) ] } - else { $flag->{'requestees'} = Bugzilla::User::match($requestee_str, 101, 1) } - - # If there is only one requestee match, make them the requestee. - if (scalar(@{$flag->{'requestees'}}) == 1) { - $flag->{'requestee'} = $flag->{'requestees'}[0]; - } - - # If there are too many requestee matches, throw an error. - elsif (scalar(@{$flag->{'requestees'}}) == 101) { - &::ThrowUserError("requestee_too_many_matches", - { requestee => $requestee_str }); - } -} - - # Ideally, we'd use Bug.pm, but it's way too heavyweight, and it can't be # made lighter without totally rewriting it, so we'll use this function # until that one gets rewritten. diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 1506e5dde..3bc02c723 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -217,6 +217,35 @@ sub match_field { # What does a "--do_not_change--" field look like (if any)? my $dontchange = $vars->{'form'}->{'dontchange'}; + # Fields can be regular expressions matching multiple form fields + # (f.e. "requestee-(\d+)"), so expand each non-literal field + # into the list of form fields it matches. + my $expanded_fields = {}; + foreach my $field_pattern (keys %{$fields}) { + # Check if the field has any non-word characters. Only those fields + # can be regular expressions, so don't expand the field if it doesn't + # have any of those characters. + if ($field_pattern =~ /^\w+$/) { + $expanded_fields->{$field_pattern} = $fields->{$field_pattern}; + } + else { + my @field_names = grep(/$field_pattern/, keys %{$vars->{'form'}}); + foreach my $field_name (@field_names) { + $expanded_fields->{$field_name} = + { type => $fields->{$field_pattern}->{'type'} }; + + # The field is a requestee field; in order for its name to show + # up correctly on the confirmation page, we need to find out + # the name of its flag type. + if ($field_name =~ /^requestee-(\d+)$/) { + $expanded_fields->{$field_name}->{'flag_type'} = + Bugzilla::FlagType::get($1); + } + } + } + } + $fields = $expanded_fields; + # Skip all of this if the option has been turned off return 1 if (&::Param('usermatchmode') eq 'off'); @@ -285,10 +314,8 @@ sub match_field { next; } - $matches->{$field}->{$query}->{'users'} = $users; - $matches->{$field}->{$query}->{'status'} = 'success'; - $matches->{$field}->{$query}->{'selecttype'} = - $fields->{$field}->{'type'}; + $matches->{$field}->{$query}->{'users'} = $users; + $matches->{$field}->{$query}->{'status'} = 'success'; # here is where it checks for multiple matches @@ -323,6 +350,7 @@ sub match_field { return 1 unless $need_confirm; # skip confirmation if not needed. $vars->{'script'} = $ENV{'SCRIPT_NAME'}; # for self-referencing URLs + $vars->{'fields'} = $fields; # fields being matched $vars->{'matches'} = $matches; # matches that were made $vars->{'matchsuccess'} = $matchsuccess; # continue or fail diff --git a/attachment.cgi b/attachment.cgi index 6e9379af1..971968b3e 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -44,6 +44,7 @@ require "CGI.pl"; # Use these modules to handle flags. use Bugzilla::Flag; use Bugzilla::FlagType; +use Bugzilla::User; # Establish a connection to the database backend. ConnectToDatabase(); @@ -109,6 +110,7 @@ elsif ($action eq "update") validateContentType() unless $::FORM{'ispatch'}; validateIsObsolete(); validatePrivate(); + Bugzilla::User::match_field({ '^requestee-(\d+)$' => { 'type' => 'single' } }); Bugzilla::Flag::validate(\%::FORM); Bugzilla::FlagType::validate(\%::FORM); update(); diff --git a/process_bug.cgi b/process_bug.cgi index 0450b7ce9..bcc5dd2c7 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -91,9 +91,10 @@ scalar(@idlist) || ThrowUserError("no_bugs_chosen"); # do a match on the fields if applicable &Bugzilla::User::match_field({ - 'qa_contact' => { 'type' => 'single' }, - 'newcc' => { 'type' => 'multi' }, - 'assigned_to' => { 'type' => 'single' }, + 'qa_contact' => { 'type' => 'single' }, + 'newcc' => { 'type' => 'multi' }, + 'assigned_to' => { 'type' => 'single' }, + '^requestee-(\d+)$' => { 'type' => 'single' }, }); # If we are duping bugs, let's also make sure that we can change diff --git a/template/en/default/global/confirm-user-match.html.tmpl b/template/en/default/global/confirm-user-match.html.tmpl index 2c1d0d2bd..4a12f44a9 100644 --- a/template/en/default/global/confirm-user-match.html.tmpl +++ b/template/en/default/global/confirm-user-match.html.tmpl @@ -23,13 +23,15 @@ [%# INTERFACE: # form: hash; the form values submitted to the script # mform: hash; the form multi-values submitted to the script + # fields: hash/record; the fields being matched, each of which has: + # type: single|multi: whether or not the user can select multiple matches + # flag_type: for flag requestee fields, the type of flag being requested # matches: hash; Hierarchical. The levels go like this: # field_name { # pattern_text { - # 'users' = @user_list (user objects) - # 'selecttype' = single|multi (selectbox type) - # 'status' = success|fail|trunc (result of search. - # 'trunc' (truncated) means max was reached) + # 'users' = @user_list (user objects) + # 'status' = success|fail|trunc (result of search. + # 'trunc' (truncated) means max was reached) # } # } # script: string; The name of the calling script, used to create a @@ -90,7 +92,7 @@ Please go back and try again with a more specific name/address. - [% ELSIF query.value.selecttype == 'single' %] + [% ELSIF fields.${field.key}.type == 'single' %] matched:
- for [% flag.type.name FILTER html %] - -

- - [% ELSIF flag.requestees.size == 1 %] - - - - [% ELSE %] -

- More than one user's name or email address contains the string - [% flag.requestee_str FILTER html %]. Choose the user - you meant from the following menu or click the back button and try - again with a more specific string. -

-

- Ask - for [% flag.type.name %] - -

- - [% END %] -[% END %] - - - - - -[% PROCESS global/footer.html.tmpl %] - -- cgit v1.2.3-65-gdbad From 3619b6e9f63fd0c1352a3eeddb8339e1bc362e57 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Sat, 9 Nov 2002 09:23:06 +0000 Subject: Fix for bug 178841: removes full paths from filenames in attachments table and prevents them from appearing again r=gerv,bbaetz a=justdave --- attachment.cgi | 16 +++++++++++++++- checksetup.pl | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 971968b3e..33f8c8542 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -276,10 +276,24 @@ sub validateData return $data; } +my $filename; sub validateFilename { defined $cgi->upload('data') || ThrowUserError("file_not_specified"); + + $filename = $cgi->upload('data'); + + # Remove path info (if any) from the file name. The browser should do this + # for us, but some are buggy. This may not work on Mac file names and could + # mess up file names with slashes in them, but them's the breaks. We only + # use this as a hint to users downloading attachments anyway, so it's not + # a big deal if it munges incorrectly occasionally. + $filename =~ s/^.*[\/\\]//; + + # Truncate the filename to 100 characters, counting from the end of the string + # to make sure we keep the filename extension. + $filename = substr($filename, -100, 100); } sub validateObsolete @@ -442,7 +456,7 @@ sub insert # Insert a new attachment into the database. # Escape characters in strings that will be used in SQL statements. - my $filename = SqlQuote($cgi->param('data')); + $filename = SqlQuote($filename); my $description = SqlQuote($::FORM{'description'}); my $contenttype = SqlQuote($::FORM{'contenttype'}); my $thedata = SqlQuote($data); diff --git a/checksetup.pl b/checksetup.pl index aa91c3a34..cd02538b3 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1353,7 +1353,7 @@ $table{attachments} = description mediumtext not null, mimetype mediumtext not null, ispatch tinyint, - filename mediumtext not null, + filename varchar(100) not null, thedata longblob not null, submitter_id mediumint not null, isobsolete tinyint not null default 0, @@ -3737,6 +3737,38 @@ if ($sth->rows == 0) { } +# 2002 November, myk@mozilla.org, bug 178841: +# +# Convert the "attachments.filename" column from a ridiculously large +# "mediumtext" to a much more sensible "varchar(100)". Also takes +# the opportunity to remove paths from existing filenames, since they +# shouldn't be there for security. Buggy browsers include them, +# and attachment.cgi now takes them out, but old ones need converting. +# +{ + my $ref = GetFieldDef("attachments", "filename"); + if ($ref->[1] ne 'varchar(100)') { + print "Removing paths from filenames in attachments table...\n"; + + $sth = $dbh->prepare("SELECT attach_id, filename FROM attachments " . + "WHERE INSTR(filename, '/') " . + "OR INSTR(filename, '\\\\')"); + $sth->execute; + + while (my ($attach_id, $filename) = $sth->fetchrow_array) { + $filename =~ s/^.*[\/\\]//; + my $quoted_filename = $dbh->quote($filename); + $dbh->do("UPDATE attachments SET filename = $quoted_filename " . + "WHERE attach_id = $attach_id"); + } + + print "Done.\n"; + + print "Resizing attachments.filename from mediumtext to varchar(100).\n"; + ChangeFieldType("attachments", "filename", "varchar(100) not null"); + } +} + # # Final checks... -- cgit v1.2.3-65-gdbad From 8816b8cb98a7902b722c00cbed6e22b12f176e74 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Sat, 9 Nov 2002 09:58:47 +0000 Subject: Fix for bug 171505: shows disabled flags in the UI r=bbaetz a=justdave --- attachment.cgi | 7 +++---- bug_form.pl | 25 ++++++++++++++++--------- template/en/default/attachment/list.html.tmpl | 9 +++------ template/en/default/flag/list.html.tmpl | 10 +++++++--- 4 files changed, 29 insertions(+), 22 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 33f8c8542..04b86dc33 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -555,12 +555,11 @@ sub edit SendSQL("SELECT product_id, component_id FROM bugs WHERE bug_id = $bugid"); my ($product_id, $component_id) = FetchSQLData(); my $flag_types = Bugzilla::FlagType::match({ 'target_type' => 'attachment' , - 'product_id' => $product_id , - 'component_id' => $component_id , - 'is_active' => 1}); + 'product_id' => $product_id , + 'component_id' => $component_id }); foreach my $flag_type (@$flag_types) { $flag_type->{'flags'} = Bugzilla::Flag::match({ 'type_id' => $flag_type->{'id'}, - 'attach_id' => $::FORM{'id'} }); + 'attach_id' => $::FORM{'id'} }); } $vars->{'flag_types'} = $flag_types; diff --git a/bug_form.pl b/bug_form.pl index 0e6b4547d..946dc4a31 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -209,8 +209,7 @@ sub show_bug { my $flag_types = Bugzilla::FlagType::match({ 'target_type' => 'bug', 'product_id' => $bug{'product_id'}, - 'component_id' => $bug{'component_id'}, - 'is_active' => 1 }); + 'component_id' => $bug{'component_id'}); foreach my $flag_type (@$flag_types) { $flag_type->{'flags'} = Bugzilla::Flag::match({ 'bug_id' => $id , @@ -219,13 +218,21 @@ sub show_bug { } $vars->{'flag_types'} = $flag_types; - # The number of types of flags that can be set on attachments - # to this bug. If none, flags won't be shown in the list of attachments. - $vars->{'num_attachment_flag_types'} = - Bugzilla::FlagType::count({ 'target_type' => 'a', - 'product_id' => $bug{'product_id'}, - 'component_id' => $bug{'component_id'}, - 'is_active' => 1 }); + # 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; diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index 59f749695..265803602 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -19,16 +19,13 @@ # Contributor(s): Myk Melez #%] -[%# Whether or not to include flags. %] -[% display_flags = num_attachment_flag_types > 0 %] -
[% column_headers.$column %]
[% IF data.$tbl.$col.$row AND data.$tbl.$col.$row > 0 %] - @@ -160,7 +161,7 @@ - [% row_total %] @@ -178,7 +179,7 @@ [% NEXT IF col == "" %] - [% col_totals.$col %] @@ -187,7 +188,7 @@ [% END %] - [% grand_total %] + [% grand_total %]
- [% IF display_flags %] + [% IF show_attachment_flags %] [% END %] @@ -55,7 +52,7 @@ - [% IF display_flags %] + [% IF show_attachment_flags %] - + [% IF (user.userid != bugassignee_id) AND UserInGroup("editbugs") %] + + + + + [% END %] diff --git a/template/en/default/filterexceptions.pl b/template/en/default/filterexceptions.pl index ba626a21b..60590d4a4 100644 --- a/template/en/default/filterexceptions.pl +++ b/template/en/default/filterexceptions.pl @@ -105,7 +105,6 @@ 'reports/components.html.tmpl' => [ 'numcols', - 'numcols - 1', 'comp.description', 'comp.initialowner', # email address 'comp.initialqacontact', # email address @@ -181,10 +180,6 @@ 'other_format.name', 'other_format.description', # 'sizeurl', - 'height + 100', - 'height - 100', - 'width + 100', - 'width - 100', 'switchbase', 'format', 'cumulate', @@ -257,7 +252,6 @@ 'list/table.html.tmpl' => [ 'id', - 'splitheader ? 2 : 1', 'abbrev.$id.title || field_descs.$id || column.title', # 'tableheader', 'bug.bug_severity', # @@ -387,9 +381,6 @@ 'dependson_ids.join(",")', 'blocked_ids.join(",")', 'dep_id', - 'hide_resolved ? 0 : 1', - 'hide_resolved ? "Show" : "Hide"', - 'realdepth < 2 || maxdepth == 1 ? "disabled" : ""', 'hide_resolved', 'realdepth < 2 ? "disabled" : ""', 'maxdepth + 1', @@ -420,7 +411,6 @@ ], 'bug/navigate.html.tmpl' => [ - 'this_bug_idx + 1', 'bug_list.first', 'bug_list.last', 'bug_list.$prev_bug', @@ -540,7 +530,6 @@ 'flag.type.name', 'flag.status', 'flag.requestee.nick', # Email - 'show_attachment_flags ? 4 : 3', 'bugid', ], @@ -553,6 +542,27 @@ 'bugid', ], +'attachment/diff-header.html.tmpl' => [ + 'attachid', + 'bugid', + 'old_url', + 'new_url', + 'oldid', + 'newid', + 'style', + 'javascript', + 'patch.id', +], + +'attachment/diff-file.html.tmpl' => [ + 'lxr_prefix', + 'file.minus_lines', + 'file.plus_lines', + 'bonsai_prefix', + 'section.old_start', + 'section_num' +], + 'admin/products/groupcontrol/confirm-edit.html.tmpl' => [ 'group.count', ], @@ -586,7 +596,6 @@ ], 'admin/flag-type/list.html.tmpl' => [ - 'type.is_active ? "active" : "inactive"', 'type.id', 'type.flag_count', ], @@ -601,7 +610,6 @@ 'account/prefs/email.html.tmpl' => [ 'watchedusers', # Email - 'useqacontact ? \'5\' : \'4\'', 'role', 'reason.name', 'reason.description', @@ -617,7 +625,6 @@ 'tab.description', 'current_tab.name', 'current_tab.description', - 'current_tab.description FILTER lower', ], ); diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 8aa3842c8..de5d60c6c 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -344,6 +344,19 @@ Valid types must be of the form foo/bar where foo is either application, audio, image, message, model, multipart, text, or video. + + [% ELSIF error == "invalid_context" %] + [% title = "Invalid Context" %] + The context [% context FILTER html %] is invalid (must be a number, + "file" or "patch"). + + [% ELSIF error == "invalid_format" %] + [% title = "Invalid Format" %] + The format "[% format FILTER html %]" is invalid (must be one of + [% FOREACH my_format = formats %] + "[% my_format FILTER html %]" + [% END %] + ). [% ELSIF error == "invalid_maxrow" %] [% title = "Invalid Max Rows" %] @@ -427,6 +440,10 @@ The query named [% queryname FILTER html %] does not exist. + [% ELSIF error == "must_be_patch" %] + [% title = "Attachment Must Be Patch" %] + Attachment #[% attach_id FILTER html %] must be a patch. + [% ELSIF error == "missing_subcategory" %] [% title = "Missing Subcategory" %] You did not specify a subcategory for this series. -- cgit v1.2.3-65-gdbad From d08d7d95cfb5813b08f4c0bbc50cc435f6cf275c Mon Sep 17 00:00:00 2001 From: "jkeiser%netscape.com" <> Date: Wed, 20 Aug 2003 07:45:39 +0000 Subject: Check for PatchReader as a part of the installation and disable the "Diff" links if it is not there (bug 215268) --- attachment.cgi | 98 ++++++++++++++++----------- checksetup.pl | 20 ++++++ show_bug.cgi | 6 ++ template/en/default/attachment/edit.html.tmpl | 16 ++++- template/en/default/attachment/list.html.tmpl | 2 +- 5 files changed, 99 insertions(+), 43 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 149ddfd21..07dbe5e51 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -467,11 +467,11 @@ sub interdiff $ENV{'PATH'} = $::diffpath; open my $interdiff_fh, "$::interdiffbin $old_filename $new_filename|"; binmode $interdiff_fh; - my ($iter, $last_iter) = setup_iterators(""); + my ($reader, $last_reader) = setup_patch_readers(""); if ($::FORM{'format'} eq "raw") { - require PatchIterator::DiffPrinter::raw; - $last_iter->sends_data_to(new PatchIterator::DiffPrinter::raw()); + require PatchReader::DiffPrinter::raw; + $last_reader->sends_data_to(new PatchReader::DiffPrinter::raw()); # Actually print out the patch print $cgi->header(-type => 'text/plain', -expires => '+3M'); @@ -487,9 +487,9 @@ sub interdiff delete $vars->{attachid}; delete $vars->{do_context}; delete $vars->{context}; - setup_template_iterator($iter, $last_iter); + setup_template_patch_reader($last_reader); } - $iter->iterate_fh($interdiff_fh, "interdiff #$::FORM{'oldid'} #$::FORM{'newid'}"); + $reader->iterate_fh($interdiff_fh, "interdiff #$::FORM{'oldid'} #$::FORM{'newid'}"); close $interdiff_fh; $ENV{'PATH'} = ''; @@ -505,10 +505,10 @@ sub get_unified_diff my ($id) = @_; # Bring in the modules we need - require PatchIterator::Raw; - require PatchIterator::FixPatchRoot; - require PatchIterator::DiffPrinter::raw; - require PatchIterator::PatchInfoGrabber; + require PatchReader::Raw; + require PatchReader::FixPatchRoot; + require PatchReader::DiffPrinter::raw; + require PatchReader::PatchInfoGrabber; require File::Temp; # Get the patch @@ -520,18 +520,29 @@ sub get_unified_diff } # Reads in the patch, converting to unified diff in a temp file - my $iter = new PatchIterator::Raw; + my $reader = new PatchReader::Raw; + my $last_reader = $reader; + # fixes patch root (makes canonical if possible) - my $fix_patch_root = new PatchIterator::FixPatchRoot(Param('cvsroot')); - $iter->sends_data_to($fix_patch_root); + if (Param('cvsroot')) { + my $fix_patch_root = new PatchReader::FixPatchRoot(Param('cvsroot')); + $last_reader->sends_data_to($fix_patch_root); + $last_reader = $fix_patch_root; + } + # Grabs the patch file info - my $patch_info_grabber = new PatchIterator::PatchInfoGrabber(); - $fix_patch_root->sends_data_to($patch_info_grabber); + my $patch_info_grabber = new PatchReader::PatchInfoGrabber(); + $last_reader->sends_data_to($patch_info_grabber); + $last_reader = $patch_info_grabber; + # Prints out to temporary file my ($fh, $filename) = File::Temp::tempfile(); - $patch_info_grabber->sends_data_to(new PatchIterator::DiffPrinter::raw($fh)); + my $raw_printer = new PatchReader::DiffPrinter::raw($fh); + $last_reader->sends_data_to($raw_printer); + $last_reader = $raw_printer; + # Iterate! - $iter->iterate_string($id, $thedata); + $reader->iterate_string($id, $thedata); return ($bugid, $description, $filename, $patch_info_grabber->patch_info()->{files}); } @@ -557,7 +568,7 @@ sub warn_if_interdiff_might_fail { return undef; } -sub setup_iterators { +sub setup_patch_readers { my ($diff_root) = @_; # @@ -568,36 +579,36 @@ sub setup_iterators { # headers=0|1 # - # Define the iterators - # The iterator that reads the patch in (whatever its format) - require PatchIterator::Raw; - my $iter = new PatchIterator::Raw; - my $last_iter = $iter; + # Define the patch readers + # The reader that reads the patch in (whatever its format) + require PatchReader::Raw; + my $reader = new PatchReader::Raw; + my $last_reader = $reader; # Fix the patch root if we have a cvs root if (Param('cvsroot')) { - require PatchIterator::FixPatchRoot; - $last_iter->sends_data_to(new PatchIterator::FixPatchRoot(Param('cvsroot'))); - $last_iter->sends_data_to->diff_root($diff_root) if defined($diff_root); - $last_iter = $last_iter->sends_data_to; + require PatchReader::FixPatchRoot; + $last_reader->sends_data_to(new PatchReader::FixPatchRoot(Param('cvsroot'))); + $last_reader->sends_data_to->diff_root($diff_root) if defined($diff_root); + $last_reader = $last_reader->sends_data_to; } # Add in cvs context if we have the necessary info to do it if ($::FORM{'context'} ne "patch" && $::cvsbin && Param('cvsroot_get')) { - require PatchIterator::AddCVSContext; - $last_iter->sends_data_to( - new PatchIterator::AddCVSContext($::FORM{'context'}, + require PatchReader::AddCVSContext; + $last_reader->sends_data_to( + new PatchReader::AddCVSContext($::FORM{'context'}, Param('cvsroot_get'))); - $last_iter = $last_iter->sends_data_to; + $last_reader = $last_reader->sends_data_to; } - return ($iter, $last_iter); + return ($reader, $last_reader); } -sub setup_template_iterator +sub setup_template_patch_reader { - my ($iter, $last_iter) = @_; + my ($last_reader) = @_; - require PatchIterator::DiffPrinter::template; + require PatchReader::DiffPrinter::template; my $format = $::FORM{'format'}; @@ -614,7 +625,7 @@ sub setup_template_iterator # Print everything out print $cgi->header(-type => 'text/html', -expires => '+3M'); - $last_iter->sends_data_to(new PatchIterator::DiffPrinter::template($template, + $last_reader->sends_data_to(new PatchReader::DiffPrinter::template($template, "attachment/diff-header.$format.tmpl", "attachment/diff-file.$format.tmpl", "attachment/diff-footer.$format.tmpl", @@ -638,17 +649,17 @@ sub diff return; } - my ($iter, $last_iter) = setup_iterators(); + my ($reader, $last_reader) = setup_patch_readers(); if ($::FORM{'format'} eq "raw") { - require PatchIterator::DiffPrinter::raw; - $last_iter->sends_data_to(new PatchIterator::DiffPrinter::raw()); + require PatchReader::DiffPrinter::raw; + $last_reader->sends_data_to(new PatchReader::DiffPrinter::raw()); # Actually print out the patch use vars qw($cgi); print $cgi->header(-type => 'text/plain', -expires => '+3M'); - $iter->iterate_string("Attachment " . $::FORM{'id'}, $thedata); + $reader->iterate_string("Attachment " . $::FORM{'id'}, $thedata); } else { @@ -674,9 +685,9 @@ sub diff $vars->{bugid} = $bugid; $vars->{attachid} = $::FORM{'id'}; $vars->{description} = $description; - setup_template_iterator($iter, $last_iter); + setup_template_patch_reader($last_reader); # Actually print out the patch - $iter->iterate_string("Attachment " . $::FORM{'id'}, $thedata); + $reader->iterate_string("Attachment " . $::FORM{'id'}, $thedata); } } @@ -937,6 +948,11 @@ sub edit $vars->{'attachments'} = \@bugattachments; $vars->{'GetBugLink'} = \&GetBugLink; + # Determine if PatchReader is installed + eval { + require PatchReader; + $vars->{'patchviewerinstalled'} = 1; + }; print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. diff --git a/checksetup.pl b/checksetup.pl index 00605415d..cde336d74 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -268,6 +268,7 @@ my $chartbase = have_vers("Chart::Base","0.99"); my $xmlparser = have_vers("XML::Parser",0); my $gdgraph = have_vers("GD::Graph",0); my $gdtextalign = have_vers("GD::Text::Align",0); +my $patchreader = have_vers("PatchReader",0); print "\n" unless $silent; if ((!$gd || !$chartbase) && !$silent) { @@ -295,6 +296,17 @@ if ((!$gd || !$gdgraph || !$gdtextalign) && !$silent) { "-e'install \"GD::Text::Align\"'\n" if !$gdtextalign; print "\n"; } +if (!$patchreader && !$silent) { + print "If you want to see pretty HTML views of patches, you should "; + print "install the \nPatchReader module, which can be downloaded at:\n"; + print "http://search.cpan.org/CPAN/authors/id/J/JK/JKEISER/PatchReader-0.9.2.tar.gz\n"; + print "When you get it, do the following to install:\n"; + print "tar xzvf PatchReader-0.9.2.tar.gz\n"; + print "cd PatchReader-0.9.2\n"; + print "perl Makefile.PL\n"; + print "make install\n\n"; +} + if (%missing) { print "\n\n"; print "Bugzilla requires some Perl modules which are either missing from your\n", @@ -461,6 +473,14 @@ END if (!LocalVarExists('interdiffbin')) { my $interdiff_executable = `which interdiff`; if ($interdiff_executable =~ /no interdiff/ || $interdiff_executable eq '') { + if (!$silent) { + print "\nOPTIONAL NOTE: If you want to "; + print "be able to use the\n 'difference between two patches"; + print "feature of Bugzilla (requires\n the PatchReader Perl module"; + print "as well), you should install\n patchutils from "; + print "http://cyberelk.net/tim/patchutils/\n\n"; + } + # If which didn't find it, set to blank $interdiff_executable = ""; } else { diff --git a/show_bug.cgi b/show_bug.cgi index 711b7201b..c7a780404 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -73,6 +73,12 @@ if ($single) { } } +# Determine if Patch Viewer is installed, for Diff link +eval { + require PatchReader; + $vars->{'patchviewerinstalled'} = 1; +}; + $vars->{'bugs'} = \@bugs; # Next bug in list (if there is one) diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 2cfc0e088..3de65766a 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -90,6 +90,7 @@ { switchToMode('edit'); } +[% IF patchviewerinstalled %] function viewDiff() { switchToMode('diff'); @@ -102,6 +103,7 @@ has_viewed_as_diff = 1; } } +[% END %] function viewRaw() { switchToMode('raw'); @@ -120,11 +122,15 @@ hideElementById('undoEditButton'); } else if (current_mode == 'raw') { hideElementById('viewFrame'); +[% IF patchviewerinstalled %] hideElementById('viewDiffButton'); +[% END %] hideElementById(has_edited ? 'redoEditButton' : 'editButton'); hideElementById('smallCommentFrame'); } else if (current_mode == 'diff') { +[% IF patchviewerinstalled %] hideElementById('viewDiffFrame'); +[% END %] hideElementById('viewRawButton'); hideElementById(has_edited ? 'redoEditButton' : 'editButton'); hideElementById('smallCommentFrame'); @@ -136,11 +142,15 @@ showElementById('undoEditButton'); } else if (mode == 'raw') { showElementById('viewFrame'); +[% IF patchviewerinstalled %] showElementById('viewDiffButton'); +[% END %] showElementById(has_edited ? 'redoEditButton' : 'editButton'); showElementById('smallCommentFrame'); } else if (mode == 'diff') { +[% IF patchviewerinstalled %] showElementById('viewDiffFrame'); +[% END %] showElementById('viewRawButton'); showElementById(has_edited ? 'redoEditButton' : 'editButton'); showElementById('smallCommentFrame'); @@ -227,7 +237,7 @@

Actions:View - [% IF ispatch %] + [% IF ispatch && patchviewerinstalled %] | Diff [% END %] @@ -243,11 +253,15 @@ +

Template->process() failed twice.
+ First error: $error
+ Second error: $error2

+ +END + } + exit; +} + 1; __END__ @@ -82,6 +128,25 @@ error handling code will unlock the database tables. In the long term, this argument will go away, to be replaced by transactional C calls. There is no timeframe for doing so, however. +=item C + +This function is used when an internal check detects an error of some sort. +This usually indicates a bug in Bugzilla, although it can occur if the user +manually constructs urls without correct parameters. + +This function's behaviour is similar to C, except that the +template used to display errors is I. In addition +if the hashref used as the optional second argument contains a key I +then the contents of the hashref (which is expected to be another hashref) will +be displayed after the error message, as a debugging aid. + +=item C + +This function should only be called if a Cprocess()> fails. +It tries another template first, because often one template being +broken or missing doesn't mean that they all are. But it falls back to +a print statement as a last-ditch error. + =back =head1 SEE ALSO diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 4a1752cd0..fe54e9d65 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -153,17 +153,17 @@ sub validate { # Make sure the flag exists. my $flag = get($id); - $flag || &::ThrowCodeError("flag_nonexistent", { id => $id }); + $flag || ThrowCodeError("flag_nonexistent", { id => $id }); # Make sure the user chose a valid status. grep($status eq $_, qw(X + - ?)) - || &::ThrowCodeError("flag_status_invalid", - { id => $id , status => $status }); + || ThrowCodeError("flag_status_invalid", + { id => $id, status => $status }); # Make sure the user didn't request the flag unless it's requestable. if ($status eq '?' && !$flag->{type}->{is_requestable}) { ThrowCodeError("flag_status_invalid", - { id => $id , status => $status }); + { id => $id, status => $status }); } # Make sure the requestee is authorized to access the bug. @@ -584,7 +584,7 @@ sub notify { $::template->process($template_file, $::vars, \$message); if (!$rv) { Bugzilla->cgi->header(); - &::ThrowTemplateError($::template->error()); + ThrowTemplateError($::template->error()); } my $delivery_mode = Param("sendmailnow") ? "" : "-ODeliveryMode=deferred"; diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index 7fbe1f142..6c3492ba2 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -198,13 +198,13 @@ sub validate { # Make sure the flag type exists. my $flag_type = get($id); $flag_type - || &::ThrowCodeError("flag_type_nonexistent", { id => $id }); + || ThrowCodeError("flag_type_nonexistent", { id => $id }); # Make sure the value of the field is a valid status. grep($status eq $_, qw(X + - ?)) - || &::ThrowCodeError("flag_status_invalid", - { id => $id , status => $status }); - + || ThrowCodeError("flag_status_invalid", + { id => $id , status => $status }); + # Make sure the user didn't request the flag unless it's requestable. if ($status eq '?' && !$flag_type->{is_requestable}) { ThrowCodeError("flag_status_invalid", diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index e795f03f3..09c47d471 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -930,7 +930,7 @@ sub init { # chart -1 is generated by other code above, not from the user- # submitted form, so we'll blindly accept any values in chart -1 if ((!$chartfields{$f}) && ($chart != -1)) { - &::ThrowCodeError("invalid_field_name", {field => $f}); + ThrowCodeError("invalid_field_name", {field => $f}); } # This is either from the internal chart (in which case we @@ -964,9 +964,10 @@ sub init { } else { # This field and this type don't work together. - $::vars->{'field'} = $params->param("field$chart-$row-$col"); - $::vars->{'type'} = $params->param("type$chart-$row-$col"); - &::ThrowCodeError("field_type_mismatch"); + ThrowCodeError("field_type_mismatch", + { field => $params->param("field$chart-$row-$col"), + type => $params->param("type$chart-$row-$col"), + }); } } if (@orlist) { diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 400d7d4fc..f7be40ab3 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -30,6 +30,7 @@ use strict; package Token; use Bugzilla::Config; +use Bugzilla::Error; use Date::Format; @@ -88,7 +89,7 @@ sub IssueEmailChangeToken { my $message; $template->process("account/email/change-old.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -99,7 +100,7 @@ sub IssueEmailChangeToken { $message = ""; $template->process("account/email/change-new.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -146,7 +147,7 @@ sub IssuePasswordToken { my $message = ""; $template->process("account/password/forgotten-password.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -176,7 +177,7 @@ sub GenerateUniqueToken { ++$tries; if ($tries > 100) { - &::ThrowCodeError("token_generation_error"); + ThrowCodeError("token_generation_error"); } $token = &::GenerateRandomPassword(); @@ -225,7 +226,7 @@ sub Cancel { my $message; $template->process("account/cancel-token.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index f5df92063..32e624913 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -33,6 +33,7 @@ use strict; package Bugzilla::User; use Bugzilla::Config; +use Bugzilla::Error; use Bugzilla::Util; ################################################################################ @@ -551,9 +552,10 @@ sub match_field { } else { # bad argument - $vars->{'argument'} = $fields->{$field}->{'type'}; - $vars->{'function'} = 'Bugzilla::User::match_field'; - &::ThrowCodeError('bad_arg'); + ThrowCodeError('bad_arg', + { argument => $fields->{$field}->{'type'}, + function => 'Bugzilla::User::match_field', + }); } for my $query (@queries) { @@ -623,7 +625,7 @@ sub match_field { print Bugzilla->cgi->header(); $::template->process("global/confirm-user-match.html.tmpl", $vars) - || &::ThrowTemplateError($::template->error()); + || ThrowTemplateError($::template->error()); exit; diff --git a/CGI.pl b/CGI.pl index 8b33ce102..75174dcba 100644 --- a/CGI.pl +++ b/CGI.pl @@ -95,14 +95,15 @@ sub CheckFormField (\%$;\@) { SendSQL("SELECT description FROM fielddefs WHERE name=" . SqlQuote($fieldname)); my $result = FetchOneColumn(); + my $field; if ($result) { - $vars->{'field'} = $result; + $field = $result; } else { - $vars->{'field'} = $fieldname; + $field = $fieldname; } - ThrowCodeError("illegal_field", undef, "abort"); + ThrowCodeError("illegal_field", { field => $field }, "abort"); } } @@ -113,8 +114,7 @@ sub CheckFormFieldDefined (\%$) { ) = @_; if (!defined $formRef->{$fieldname}) { - $vars->{'field'} = $fieldname; - ThrowCodeError("undefined_field"); + ThrowCodeError("undefined_field", { field => $fieldname }); } } @@ -241,81 +241,6 @@ sub PutFooter { || ThrowTemplateError($::template->error()); } -############################################################################### -# Error handling -# -# If you are doing incremental output, set $vars->{'header_done'} once you've -# done the header. -# -# You can call Throw*Error with extra template variables in one pass by using -# the $extra_vars hash reference parameter: -# ThrowUserError("some_tag", { bug_id => $bug_id, size => 127 }); -############################################################################### - -# For "this shouldn't happen"-type places in the code. -# The contents of $extra_vars get printed out in the template - useful for -# debugging info. -sub ThrowCodeError { - ($vars->{'error'}, my $extra_vars, my $unlock_tables) = (@_); - - SendSQL("UNLOCK TABLES") if $unlock_tables; - - # If we don't have this test here, then the %@extra_vars vivifies - # the hashref, and then setting $vars->{'variables'} uses an empty hashref - # so the error template prints out a bogus header for the empty hash - if (defined $extra_vars) { - # Copy the extra_vars into the vars hash - foreach my $var (keys %$extra_vars) { - $vars->{$var} = $extra_vars->{$var}; - } - - # We may one day log something to file here also. - $vars->{'variables'} = $extra_vars; - } - - print Bugzilla->cgi->header(); - $template->process("global/code-error.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - - exit; -} - -# This function should only be called if a template->process() fails. -# It tries another template first, because often one template being -# broken or missing doesn't mean that they all are. But it falls back on -# a print statement. -# The Content-Type will already have been printed. -sub ThrowTemplateError { - ($vars->{'template_error_msg'}) = (@_); - $vars->{'error'} = "template_error"; - - # Try a template first; but if this one fails too, fall back - # on plain old print statements. - if (!$template->process("global/code-error.html.tmpl", $vars)) { - my $maintainer = Param('maintainer'); - my $error = html_quote($vars->{'template_error_msg'}); - my $error2 = html_quote($template->error()); - print < -

- Bugzilla has suffered an internal error. Please save this page and - send it to $maintainer with details of what you were doing at the - time this message appeared. -

- -

Template->process() failed twice.
- First error: $error
- Second error: $error2

- -END - } - - exit; -} - sub CheckIfVotedConfirmed { my ($id, $who) = (@_); SendSQL("SELECT bugs.votes, bugs.bug_status, products.votestoconfirm, " . diff --git a/Token.pm b/Token.pm index 400d7d4fc..f7be40ab3 100644 --- a/Token.pm +++ b/Token.pm @@ -30,6 +30,7 @@ use strict; package Token; use Bugzilla::Config; +use Bugzilla::Error; use Date::Format; @@ -88,7 +89,7 @@ sub IssueEmailChangeToken { my $message; $template->process("account/email/change-old.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -99,7 +100,7 @@ sub IssueEmailChangeToken { $message = ""; $template->process("account/email/change-new.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -146,7 +147,7 @@ sub IssuePasswordToken { my $message = ""; $template->process("account/password/forgotten-password.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; @@ -176,7 +177,7 @@ sub GenerateUniqueToken { ++$tries; if ($tries > 100) { - &::ThrowCodeError("token_generation_error"); + ThrowCodeError("token_generation_error"); } $token = &::GenerateRandomPassword(); @@ -225,7 +226,7 @@ sub Cancel { my $message; $template->process("account/cancel-token.txt.tmpl", $vars, \$message) - || &::ThrowTemplateError($template->error()); + || ThrowTemplateError($template->error()); open SENDMAIL, "|/usr/lib/sendmail -t -i"; print SENDMAIL $message; diff --git a/attachment.cgi b/attachment.cgi index 07dbe5e51..d020b4f57 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -153,7 +153,7 @@ elsif ($action eq "update") } else { - ThrowCodeError("unknown_action"); + ThrowCodeError("unknown_action", { action => $action }); } exit; @@ -302,8 +302,8 @@ sub validateContentType } else { - $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; - ThrowCodeError("illegal_content_type_method"); + ThrowCodeError("illegal_content_type_method", + { contenttypemethod => $::FORM{'contenttypemethod'} }); } if ( $::FORM{'contenttype'} !~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ ) @@ -387,13 +387,11 @@ sub validateObsolete # Make sure the attachment id is valid and the user has permissions to view # the bug to which it is attached. foreach my $attachid (@{$::MFORM{'obsolete'}}) { - # my $vars after ThrowCodeError is updated to not use the global - # vars hash - + my $vars = {}; $vars->{'attach_id'} = $attachid; detaint_natural($attachid) - || ThrowCodeError("invalid_attach_id_to_obsolete"); + || ThrowCodeError("invalid_attach_id_to_obsolete", $vars); SendSQL("SELECT bug_id, isobsolete, description FROM attachments WHERE attach_id = $attachid"); @@ -410,12 +408,12 @@ sub validateObsolete { $vars->{'my_bug_id'} = $::FORM{'bugid'}; $vars->{'attach_bug_id'} = $bugid; - ThrowCodeError("mismatched_bug_ids_on_obsolete"); + ThrowCodeError("mismatched_bug_ids_on_obsolete", $vars); } if ( $isobsolete ) { - ThrowCodeError("attachment_already_obsolete"); + ThrowCodeError("attachment_already_obsolete", $vars); } # Check that the user can modify this attachment diff --git a/buglist.cgi b/buglist.cgi index bff5e75e0..f56b007ad 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -544,14 +544,14 @@ if ($order) { # Accept an order fragment matching a column name, with # asc|desc optionally following (to specify the direction) if (!grep($fragment =~ /^\Q$_\E(\s+(asc|desc))?$/, @columnnames)) { - $vars->{'fragment'} = $fragment; + my $vars = { fragment => $fragment }; if ($order_from_cookie) { $cgi->send_cookie(-name => 'LASTORDER', -expires => 'Tue, 15-Sep-1998 21:49:00 GMT'); - ThrowCodeError("invalid_column_name_cookie"); + ThrowCodeError("invalid_column_name_cookie", $vars); } else { - ThrowCodeError("invalid_column_name_form"); + ThrowCodeError("invalid_column_name_form", $vars); } } } diff --git a/post_bug.cgi b/post_bug.cgi index f0d4f0816..9a1ea747f 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -477,7 +477,7 @@ $vars->{'id'} = $id; my $bug = new Bug($id, $::userid); $vars->{'bug'} = $bug; -ThrowCodeError("bug_error") if $bug->error; +ThrowCodeError("bug_error", { bug => $bug }) if $bug->error; $vars->{'sentmail'} = []; diff --git a/process_bug.cgi b/process_bug.cgi index cf9abf4cb..fde735434 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -976,9 +976,8 @@ SWITCH: for ($::FORM{'knob'}) { last SWITCH; }; - - $vars->{'action'} = $::FORM{'knob'}; - ThrowCodeError("unknown_action"); + + ThrowCodeError("unknown_action", { action => $::FORM{'knob'} }); } @@ -1746,8 +1745,7 @@ foreach my $id (@idlist) { 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; + ThrowCodeError("bug_error", { bug => $bug }) if $bug->error; $template->process("bug/process/next.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/request.cgi b/request.cgi index ae137959d..3672e0449 100755 --- a/request.cgi +++ b/request.cgi @@ -165,10 +165,10 @@ sub queue { push(@criteria, "bugs.component_id = $component_id"); push(@excluded_columns, 'component') unless $::FORM{'do_union'}; } - else { ThrowCodeError("unknown_component", { %::FORM }) } + else { ThrowCodeError("unknown_component", { component => $::FORM{component} }) } } } - else { ThrowCodeError("unknown_product", { %::FORM }) } + else { ThrowCodeError("unknown_product", { product => $::FORM{product} }) } } # Filter results by flag types. @@ -281,7 +281,8 @@ sub validateStatus { return if !defined($::FORM{'status'}); grep($::FORM{'status'} eq $_, qw(? +- + - all)) - || ThrowCodeError("flag_status_invalid", { status => $::FORM{'status'} }); + || ThrowCodeError("flag_status_invalid", + { status => $::FORM{'status'} }); } sub validateGroup { diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index 939c0c4dc..73501672b 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -39,7 +39,7 @@ [% error_message = BLOCK %] [% IF error == "action_unrecognized" %] - I don't recognize the value ([% variables.action FILTER html %]) + I don't recognize the value ([% action FILTER html %]) of the action variable. [% ELSIF error == "attachment_already_obsolete" %] @@ -85,9 +85,6 @@ Charts will not work without the GD Perl module being installed. Run checksetup.pl for installation instructions. - [% ELSIF error == "group_bit_invalid" %] - One of the group bits submitted was invalid. - [% ELSIF error == "illegal_content_type_method" %] Your form submission got corrupted somehow. The content method field, which specifies how the content type gets determined, @@ -142,27 +139,31 @@ [% terms.bug %] [%+ my_bug_id FILTER html %]. [% ELSIF error == "flag_nonexistent" %] - There is no flag with ID #[% variables.id FILTER html %]. + There is no flag with ID #[% id FILTER html %]. [% ELSIF error == "flag_status_invalid" %] - The flag status [% variables.status FILTER html %] is invalid. + The flag status [% status FILTER html %] + [% IF id %] + for flag ID #[% id FILTER html %] + [% END %] + is invalid. [% ELSIF error == "flag_type_component_nonexistent" %] - The component [% variables.component FILTER html %] does not exist - in the product [% variables.product FILTER html %]. + The component [% component FILTER html %] does not exist + in the product [% product FILTER html %]. [% ELSIF error == "flag_type_component_without_product" %] A component was selected without a product being selected. [% ELSIF error == "flag_type_id_invalid" %] - The flag type ID [% variables.id FILTER html %] is not + The flag type ID [% id FILTER html %] is not a positive integer. [% ELSIF error == "flag_type_nonexistent" %] - There is no flag type with the ID [% variables.id FILTER html %]. + There is no flag type with the ID [% id FILTER html %]. [% ELSIF error == "flag_type_product_nonexistent" %] - The product [% variables.product FILTER html %] does not exist. + The product [% product FILTER html %] does not exist. [% ELSIF error == "flag_type_target_type_invalid" %] The target type was neither [% terms.bug %] nor attachment @@ -171,10 +172,6 @@ [% ELSIF error == "invalid_field_name" %] Can't use [% field FILTER html %] as a field name. - [% ELSIF error == "invalid_output_type" %] - [% title = "Invalid Output Type" %] - Invalid output type [% type FILTER html %]. - [% ELSIF error == "missing_bug_id" %] No [% terms.bug %] ID was given. @@ -195,10 +192,10 @@ The group field [% group FILTER html %] is invalid. [% ELSIF error == "report_axis_invalid" %] - [% variables.val FILTER html %] is not a valid value for - [%+ IF variables.fld == "x" %]the horizontal axis - [%+ ELSIF variables.fld == "y" %]the vertical axis - [%+ ELSIF variables.fld == "z" %]the multiple tables/images + [% val FILTER html %] is not a valid value for + [%+ IF fld == "x" %]the horizontal axis + [%+ ELSIF fld == "y" %]the vertical axis + [%+ ELSIF fld == "z" %]the multiple tables/images [%+ ELSE %]a report axis[% END %] field. [% ELSIF error == "token_generation_error" %] @@ -222,21 +219,19 @@ [% ELSIF error == "unknown_component" %] [% title = "Unknown Component" %] - There is no component named [% variables.component FILTER html %]. + There is no component named [% component FILTER html %]. [% ELSIF error == "unknown_product" %] [% title = "Unknown Product" %] - There is no product named [% variables.product FILTER html %]. + There is no product named [% product FILTER html %]. [% ELSE %] - [%# Give sensible error if error functions are used incorrectly. - #%] - You are using [% terms.Bugzilla %]'s ThrowCodeError() function incorrectly. - You passed in the string '[% error FILTER html %]'. The correct use is to - pass in a tag, and define that tag in the file code-error.html.tmpl.
-
- If you are a [% terms.Bugzilla %] end-user seeing this message, please save this - page and send it to [% Param('maintainer') %]. + [% title = "Internal error" %] + An internal error has occured, but [% terms.Bugzilla %] doesn't know + what [% error FILTER html %] means. + + If you are a [% terms.Bugzilla %] end-user seeing this message, please save + this page and send it to [% Param('maintainer') %]. [% END %] [% END %] diff --git a/token.cgi b/token.cgi index 25f68b70e..76c1a2e11 100755 --- a/token.cgi +++ b/token.cgi @@ -146,7 +146,7 @@ if ($::action eq 'reqpw') { # If the action that the user wants to take (specified in the "a" form field) # is none of the above listed actions, display an error telling the user # that we do not understand what they would like to do. - ThrowCodeError("unknown_action"); + ThrowCodeError("unknown_action", { action => $::action }); } exit; -- cgit v1.2.3-65-gdbad From 376c76bd00d1eefd68c1013bf43cb6cbb3be653b Mon Sep 17 00:00:00 2001 From: "kiko%async.com.br" <> Date: Wed, 29 Oct 2003 09:58:13 +0000 Subject: Fix for bug 111522: Provide ability to specify MIME type of attachment when downloading. Adds a 'ctype' argument to attachment.cgi which allows one to override the content-type when viewing an attachment. Original patch by Alex Vincent ; I changed it a tiny bit. r=kiko, myk. a=justdave. --- attachment.cgi | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index d020b4f57..1f855d367 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -22,6 +22,7 @@ # Myk Melez # Daniel Raichle # Dave Miller +# Alexander J. Vincent ################################################################################ # Script Initialization @@ -433,6 +434,15 @@ sub view # Retrieve the attachment content and its content type from the database. SendSQL("SELECT mimetype, filename, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); my ($contenttype, $filename, $thedata) = FetchSQLData(); + + # Bug 111522: allow overriding content-type manually in the posted $::FORM. + if ($::FORM{'ctype'}) + { + $::FORM{'contenttypemethod'} = 'manual'; + $::FORM{'contenttypeentry'} = $::FORM{'ctype'}; + validateContentType(); + $contenttype = $::FORM{'ctype'}; + } # Return the appropriate HTTP response headers. $filename =~ s/^.*[\/\\]//; -- cgit v1.2.3-65-gdbad From 8e646f42ca9aff92ae92dd825b41bb61b273d57e Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Sun, 2 Nov 2003 15:36:10 +0000 Subject: Bug 111522: Provide ability to specify MIME type of attachment when downloading - correction of url parameter name to avoid usage conflicts with other parts of Bugzilla Patch by Alex Vincent r= justdave, a= justdave --- attachment.cgi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 1f855d367..7063609ee 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -436,12 +436,12 @@ sub view my ($contenttype, $filename, $thedata) = FetchSQLData(); # Bug 111522: allow overriding content-type manually in the posted $::FORM. - if ($::FORM{'ctype'}) + if ($::FORM{'content_type'}) { $::FORM{'contenttypemethod'} = 'manual'; - $::FORM{'contenttypeentry'} = $::FORM{'ctype'}; + $::FORM{'contenttypeentry'} = $::FORM{'content_type'}; validateContentType(); - $contenttype = $::FORM{'ctype'}; + $contenttype = $::FORM{'content_type'}; } # Return the appropriate HTTP response headers. -- cgit v1.2.3-65-gdbad From 128218230d7c43f7f574e1ad8b23b3564e3341b1 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Fri, 14 Nov 2003 07:44:06 +0000 Subject: Bug 219358 - Make attachments with all supported MIME types viewable in the edit page. Patch by gerv; r=kiko, r,a=justdave. --- attachment.cgi | 48 ++++++++++++++++++++------- template/en/default/attachment/edit.html.tmpl | 9 +++-- 2 files changed, 43 insertions(+), 14 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 7063609ee..c61b999e0 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -45,6 +45,7 @@ require "CGI.pl"; use Bugzilla::Flag; use Bugzilla::FlagType; use Bugzilla::User; +use Bugzilla::Util; # Establish a connection to the database backend. ConnectToDatabase(); @@ -420,7 +421,38 @@ sub validateObsolete # Check that the user can modify this attachment validateCanEdit($attachid); } +} + +# Returns 1 if the parameter is a content-type viewable in this browser +# Note that we don't use $cgi->Accept()'s ability to check if a content-type +# matches, because this will return a value even if it's matched by the generic +# */* which most browsers add to the end of their Accept: headers. +sub isViewable +{ + my $contenttype = trim(shift); + + # We assume we can view all text and image types + if ($contenttype =~ /^(text|image)\//) { + return 1; + } + + # Mozilla can view XUL. Note the trailing slash on the Gecko detection to + # avoid sending XUL to Safari. + if (($contenttype =~ /^application\/vnd\.mozilla\./) && + ($cgi->user_agent() =~ /Gecko\//)) + { + return 1; + } + # If it's not one of the above types, we check the Accept: header for any + # types mentioned explicitly. + my $accept = join(",", $cgi->Accept()); + + if ($accept =~ /^(.*,)?\Q$contenttype\E(,.*)?$/) { + return 1; + } + + return 0; } ################################################################################ @@ -718,13 +750,9 @@ sub viewall { my %a; # the attachment hash ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, - $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}) = FetchSQLData(); - - # Flag attachments as to whether or not they can be viewed (as opposed to - # being downloaded). Currently I decide they are viewable if their MIME type - # is either text/*, image/*, or application/vnd.mozilla.*. - # !!! Yuck, what an ugly hack. Fix it! - $a{'isviewable'} = ( $a{'contenttype'} =~ /^(text|image|application\/vnd\.mozilla\.)/ ); + $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}) + = FetchSQLData(); + $a{'isviewable'} = isViewable($a{'contenttype'}); # Add the hash representing the attachment to the array of attachments. push @attachments, \%a; @@ -915,11 +943,7 @@ sub edit FROM attachments WHERE attach_id = $::FORM{'id'}"); my ($description, $contenttype, $filename, $bugid, $ispatch, $isobsolete, $isprivate) = FetchSQLData(); - # Flag attachment as to whether or not it can be viewed (as opposed to - # being downloaded). Currently I decide it is viewable if its content - # type is either text/.* or application/vnd.mozilla.*. - # !!! Yuck, what an ugly hack. Fix it! - my $isviewable = ( $contenttype =~ /^(text|image|application\/vnd\.mozilla\.)/ ); + my $isviewable = isViewable($contenttype); # Retrieve a list of attachments for this bug as well as a summary of the bug # to use in a navigation bar across the top of the screen. diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 3de65766a..fba79ee25 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -270,8 +270,13 @@ [% ELSE %]
[% END %] -- cgit v1.2.3-65-gdbad From 4831ddd26807c9868baec3645255e2b762547ac0 Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Wed, 4 Feb 2004 09:52:20 +0000 Subject: Bug 232993: Quote the filenames in the Content-disposition header when downloading attachments. This allows spaces to be used in filenames, and fixes compliance with RFCs 2183, 2045, and 822. r= myk, a= justdave --- attachment.cgi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index c61b999e0..fd9983841 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -480,8 +480,12 @@ sub view $filename =~ s/^.*[\/\\]//; my $filesize = length($thedata); + # escape quotes and backslashes in the filename, per RFCs 2045/822 + $filename =~ s/\\/\\\\/g; # escape backslashes + $filename =~ s/"/\\"/g; # escape quotes + print Bugzilla->cgi->header(-type=>"$contenttype; name=\"$filename\"", - -content_disposition=> "inline; filename=$filename", + -content_disposition=> "inline; filename=\"$filename\"", -content_length => $filesize); print $thedata; -- cgit v1.2.3-65-gdbad From 0b01a89cc8901ad0217455563c58b26c10f6dad7 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Fri, 6 Feb 2004 02:14:01 +0000 Subject: Fix for bug 127995: shows the size of attachments in the show bug and attachment interfaces. Patch by Dave Swegen r=myk a=myk --- Attachment.pm | 7 ++++--- Bugzilla/Attachment.pm | 7 ++++--- Bugzilla/Template.pm | 24 ++++++++++++++++++++++ attachment.cgi | 12 ++++++----- checksetup.pl | 1 + t/004template.t | 1 + t/008filter.t | 3 ++- template/en/default/attachment/edit.html.tmpl | 1 + template/en/default/attachment/list.html.tmpl | 4 +++- .../en/default/attachment/show-multiple.html.tmpl | 3 ++- 10 files changed, 49 insertions(+), 14 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index ea5cd531c..84979d3ea 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -77,7 +77,8 @@ sub query # of hashes in which each hash represents a single attachment. &::SendSQL(" SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), - mimetype, description, ispatch, isobsolete, isprivate, submitter_id + mimetype, description, ispatch, isobsolete, isprivate, + submitter_id, LENGTH(thedata) FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); @@ -85,8 +86,8 @@ sub query my %a; my $submitter_id; ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, - $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id) - = &::FetchSQLData(); + $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id, + $a{'datasize'}) = &::FetchSQLData(); # Retrieve a list of flags for this attachment. $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index ea5cd531c..84979d3ea 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -77,7 +77,8 @@ sub query # of hashes in which each hash represents a single attachment. &::SendSQL(" SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), - mimetype, description, ispatch, isobsolete, isprivate, submitter_id + mimetype, description, ispatch, isobsolete, isprivate, + submitter_id, LENGTH(thedata) FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); @@ -85,8 +86,8 @@ sub query my %a; my $submitter_id; ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, - $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id) - = &::FetchSQLData(); + $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id, + $a{'datasize'}) = &::FetchSQLData(); # Retrieve a list of flags for this attachment. $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index d370627d3..c123154bb 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -258,6 +258,30 @@ sub create { return $var; } , + # Format a filesize in bytes to a human readable value + unitconvert => sub + { + my ($data) = @_; + my $retval = ""; + my %units = ( + 'KB' => 1024, + 'MB' => 1024 * 1024, + 'GB' => 1024 * 1024 * 1024, + ); + + if ($data < 1024) { + return "$data bytes"; + } + else { + my $u; + foreach $u ('GB', 'MB', 'KB') { + if ($data >= $units{$u}) { + return sprintf("%.2f %s", $data/$units{$u}, $u); + } + } + } + }, + # Format a time for display (more info in Bugzilla::Util) time => \&Bugzilla::Util::format_time, diff --git a/attachment.cgi b/attachment.cgi index fd9983841..8df562120 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -746,7 +746,8 @@ sub viewall $privacy = "AND isprivate < 1 "; } SendSQL("SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), - mimetype, description, ispatch, isobsolete, isprivate + mimetype, description, ispatch, isobsolete, isprivate, + LENGTH(thedata) FROM attachments WHERE bug_id = $::FORM{'bugid'} $privacy ORDER BY attach_id"); my @attachments; # the attachments array @@ -754,8 +755,8 @@ sub viewall { my %a; # the attachment hash ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, - $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}) - = FetchSQLData(); + $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, + $a{'datasize'}) = FetchSQLData(); $a{'isviewable'} = isViewable($a{'contenttype'}); # Add the hash representing the attachment to the array of attachments. @@ -943,9 +944,9 @@ sub edit # Users cannot edit the content of the attachment itself. # Retrieve the attachment from the database. - SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isobsolete, isprivate + SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isobsolete, isprivate, LENGTH(thedata) FROM attachments WHERE attach_id = $::FORM{'id'}"); - my ($description, $contenttype, $filename, $bugid, $ispatch, $isobsolete, $isprivate) = FetchSQLData(); + my ($description, $contenttype, $filename, $bugid, $ispatch, $isobsolete, $isprivate, $datasize) = FetchSQLData(); my $isviewable = isViewable($contenttype); @@ -980,6 +981,7 @@ sub edit $vars->{'ispatch'} = $ispatch; $vars->{'isobsolete'} = $isobsolete; $vars->{'isprivate'} = $isprivate; + $vars->{'datasize'} = $datasize; $vars->{'isviewable'} = $isviewable; $vars->{'attachments'} = \@bugattachments; $vars->{'GetBugLink'} = \&GetBugLink; diff --git a/checksetup.pl b/checksetup.pl index 4385accfc..9726986e0 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1111,6 +1111,7 @@ END quoteUrls => sub { return $_; }, bug_link => [ sub { return sub { return $_; } }, 1], csv => sub { return $_; }, + unitconvert => sub { return $_; }, time => sub { return $_; }, none => sub { return $_; } , }, diff --git a/t/004template.t b/t/004template.t index 3b41282cc..6c753c0bd 100644 --- a/t/004template.t +++ b/t/004template.t @@ -101,6 +101,7 @@ foreach my $include_path (@include_paths) { quoteUrls => sub { return $_ } , bug_link => [ sub { return sub { return $_; } }, 1] , csv => sub { return $_ } , + unitconvert => sub { return $_ }, time => sub { return $_ } , none => sub { return $_ } , }, diff --git a/t/008filter.t b/t/008filter.t index 8e0ca2d04..722802bb8 100644 --- a/t/008filter.t +++ b/t/008filter.t @@ -202,7 +202,8 @@ sub directive_ok { # Note: If a single directive prints two things, and only one is # filtered, we may not catch that case. return 1 if $directive =~ /FILTER\ (html|csv|js|url_quote|css_class_quote| - quoteUrls|time|uri|xml|lower|none)/x; + quoteUrls|time|uri|xml|lower| + unitconvert|none)/x; return 0; } diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 671842152..19cc06550 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -211,6 +211,7 @@ Filename:

+ Size: [% datasize FILTER unitconvert %]
MIME Type:

diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index 407ef8dfb..1ef6cab12 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -25,6 +25,7 @@
+ [% IF show_attachment_flags %] [% END %] @@ -47,6 +48,7 @@ + [% IF show_attachment_flags %] - [% IF attachments.size %] diff --git a/template/en/default/attachment/show-multiple.html.tmpl b/template/en/default/attachment/show-multiple.html.tmpl index 3159b8e87..48f03dff1 100644 --- a/template/en/default/attachment/show-multiple.html.tmpl +++ b/template/en/default/attachment/show-multiple.html.tmpl @@ -40,7 +40,7 @@
Attachment Type CreatedFlagsActions[% attachment.date %] [% IF attachment.flags.size == 0 %] none @@ -85,7 +82,7 @@ [% END %]
+ Create a New Attachment (proposed patch, testcase, etc.) diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl index f809a4e0e..abfac6fcc 100644 --- a/template/en/default/flag/list.html.tmpl +++ b/template/en/default/flag/list.html.tmpl @@ -33,9 +33,13 @@ -- cgit v1.2.3-65-gdbad From be1bdf3972e95f617138f631e466875e7bd0b34c Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Mon, 11 Nov 2002 20:36:22 +0000 Subject: Fix for bug 179334: updates the setter consistently. also fixes numerous other bugs in the RT code. r=bbaetz a=myk --- Bugzilla/Flag.pm | 55 ++++++++++++++++++------ Bugzilla/User.pm | 11 +++-- attachment.cgi | 3 +- process_bug.cgi | 8 ++-- template/en/default/flag/list.html.tmpl | 76 ++++++++++++++++++++++----------- 5 files changed, 108 insertions(+), 45 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 7395be09d..914251968 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -183,6 +183,7 @@ sub process { my @old_summaries; foreach my $flag (@$flags) { my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; + $summary .= "($flag->{'requestee'}->{'nick'})" if $flag->{'requestee'}; push(@old_summaries, $summary); } @@ -221,6 +222,7 @@ sub process { my @new_summaries; foreach my $flag (@$flags) { my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; + $summary .= "($flag->{'requestee'}->{'nick'})" if $flag->{'requestee'}; push(@new_summaries, $summary); } @@ -268,8 +270,10 @@ sub create { $timestamp)"); # Send an email notifying the relevant parties about the flag creation. - if ($flag->{'requestee'} && $flag->{'requestee'}->email_prefs->{'FlagRequestee'} - || $flag->{'type'}->{'cc_list'}) { + if ($flag->{'requestee'} + && ($flag->{'requestee'}->email_prefs->{'FlagRequestee'} + || $flag->{'type'}->{'cc_list'})) + { notify($flag, "request/created-email.txt.tmpl"); } } @@ -299,15 +303,21 @@ sub modify { # Extract a list of flags from the form data. my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data); - # Loop over flags and update their record in the database. + # Loop over flags and update their record in the database if necessary. + # Two kinds of changes can happen to a flag: it can be set to a different + # state, and someone else can be asked to set it. We take care of both + # those changes. my @flags; foreach my $id (@ids) { my $flag = get($id); + my $status = $data->{"flag-$id"}; - + my $requestee_email = $data->{"requestee-$id"}; + # Ignore flags the user didn't change. - next if $status eq $flag->{'status'}; - + next if ($status eq $flag->{'status'} && $flag->{'requestee'} + && $requestee_email eq $flag->{'requestee'}->{'email'}); + # Since the status is validated, we know it's safe, but it's still # tainted, so we have to detaint it before using it in a query. &::trick_taint($status); @@ -315,6 +325,7 @@ sub modify { if ($status eq '+' || $status eq '-') { &::SendSQL("UPDATE flags SET setter_id = $::userid , + requestee_id = NULL , status = '$status' , modification_date = $timestamp WHERE id = $flag->{'id'}"); @@ -328,10 +339,28 @@ sub modify { } } elsif ($status eq '?') { + # Get the requestee, if any. + my $requestee_id = "NULL"; + if ($requestee_email) { + $requestee_id = &::DBname_to_id($requestee_email); + $flag->{'requestee'} = new Bugzilla::User($requestee_id); + } + + # Update the database with the changes. &::SendSQL("UPDATE flags - SET status = '$status' , + SET setter_id = $::userid , + requestee_id = $requestee_id , + status = '$status' , modification_date = $timestamp WHERE id = $flag->{'id'}"); + + # Send an email notifying the relevant parties about the request. + if ($flag->{'requestee'} + && ($flag->{'requestee'}->email_prefs->{'FlagRequestee'} + || $flag->{'type'}->{'cc_list'})) + { + notify($flag, "request/created-email.txt.tmpl"); + } } # The user unset the flag, so delete it from the database. elsif ($status eq 'X') { @@ -390,12 +419,12 @@ sub FormToNewFlags { status => $status }; - my $requestee_str = $data->{"requestee-$type_id"} || $data->{'requestee'}; - if ($requestee_str) { - my $requestee_id = &::DBname_to_id($requestee_str); - $requestee_id - || &::ThrowUserError("invalid_username", {name => $requestee_str}); - $flag->{'requestee'} = new Bugzilla::User($requestee_id); + if ($status eq "?") { + my $requestee = $data->{"requestee_type-$type_id"}; + if ($requestee) { + my $requestee_id = &::DBname_to_id($requestee); + $flag->{'requestee'} = new Bugzilla::User($requestee_id); + } } # Add the flag to the array of flags. diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 3bc02c723..7cf05d935 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -234,10 +234,15 @@ sub match_field { $expanded_fields->{$field_name} = { type => $fields->{$field_pattern}->{'type'} }; - # The field is a requestee field; in order for its name to show - # up correctly on the confirmation page, we need to find out - # the name of its flag type. + # The field is a requestee field; in order for its name + # to show up correctly on the confirmation page, we need + # to find out the name of its flag type. if ($field_name =~ /^requestee-(\d+)$/) { + my $flag = Bugzilla::Flag::get($1); + $expanded_fields->{$field_name}->{'flag_type'} = + $flag->{'type'}; + } + elsif ($field_name =~ /^requestee_type-(\d+)$/) { $expanded_fields->{$field_name}->{'flag_type'} = Bugzilla::FlagType::get($1); } diff --git a/attachment.cgi b/attachment.cgi index 04b86dc33..4c5737156 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -110,7 +110,8 @@ elsif ($action eq "update") validateContentType() unless $::FORM{'ispatch'}; validateIsObsolete(); validatePrivate(); - Bugzilla::User::match_field({ '^requestee-(\d+)$' => { 'type' => 'single' } }); + Bugzilla::User::match_field({ '^requestee(_type)?-(\d+)$' => + { 'type' => 'single' } }); Bugzilla::Flag::validate(\%::FORM); Bugzilla::FlagType::validate(\%::FORM); update(); diff --git a/process_bug.cgi b/process_bug.cgi index a62320fd2..a0ed799c3 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -91,10 +91,10 @@ scalar(@idlist) || ThrowUserError("no_bugs_chosen"); # do a match on the fields if applicable &Bugzilla::User::match_field({ - 'qa_contact' => { 'type' => 'single' }, - 'newcc' => { 'type' => 'multi' }, - 'assigned_to' => { 'type' => 'single' }, - '^requestee-(\d+)$' => { 'type' => 'single' }, + 'qa_contact' => { 'type' => 'single' }, + 'newcc' => { 'type' => 'multi' }, + 'assigned_to' => { 'type' => 'single' }, + '^requestee(_type)?-(\d+)$' => { 'type' => 'single' }, }); # If we are duping bugs, let's also make sure that we can change diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl index c3d760eed..67cb0b815 100644 --- a/template/en/default/flag/list.html.tmpl +++ b/template/en/default/flag/list.html.tmpl @@ -20,33 +20,53 @@ #%] +[%# We list flags by looping twice over the flag types relevant for the bug. + # In the first loop, we display existing flags and then, for active types, + # we display UI for adding new flags. In the second loop, we display UI + # for adding additional new flags for those types for which a flag already + # exists but which are multiplicable (can have multiple flags of the type + # on a single bug/attachment). + #%] + [% FOREACH type = flag_types %] @@ -59,7 +79,8 @@ [% type.name FILTER html %] @@ -89,7 +113,8 @@ @@ -117,7 +143,8 @@ -- cgit v1.2.3-65-gdbad From e008d25513ff2d4c16c4aa49f48bfe188f9759ba Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Tue, 19 Nov 2002 09:14:49 +0000 Subject: Fix for bug 179876: Labels the "Requestee" field to reduce confusion about its purpose. r=bbaetz a=myk --- attachment.cgi | 1 + bug_form.pl | 1 + css/edit_bug.css | 1 + template/en/default/attachment/edit.html.tmpl | 3 +- template/en/default/bug/edit.html.tmpl | 1 - template/en/default/flag/list.html.tmpl | 82 ++++++++++++++++----------- 6 files changed, 53 insertions(+), 36 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 4c5737156..6e925e69a 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -563,6 +563,7 @@ sub edit 'attach_id' => $::FORM{'id'} }); } $vars->{'flag_types'} = $flag_types; + $vars->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types); # Define the variables and functions that will be passed to the UI template. $vars->{'attachid'} = $::FORM{'id'}; diff --git a/bug_form.pl b/bug_form.pl index 027b14e77..c620f03fd 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -217,6 +217,7 @@ sub show_bug { '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 diff --git a/css/edit_bug.css b/css/edit_bug.css index 1b6453745..a836f7e4c 100644 --- a/css/edit_bug.css +++ b/css/edit_bug.css @@ -1,3 +1,4 @@ .bz_private { color: darkred ; background : #f3eeee ; } +table#flags th, table#flags td { vertical-align: baseline; text-align: left; } diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 32449f041..8cd92774b 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -33,7 +33,7 @@ table.attachment_info td { text-align: left; vertical-align: top; } #noview { text-align: left; vertical-align: center; } - table#flags th, table#flags td { font-size: small; vertical-align: baseline; } + table#flags th, table#flags td { font-size: small; vertical-align: baseline; text-align: left; } " %] @@ -173,7 +173,6 @@ [% END %] [% IF flag_types.size > 0 %] - Flags:
[% PROCESS "flag/list.html.tmpl" bug_id=bugid attach_id=attachid %]
[% END %] diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index 162b40a7f..62e25f10a 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -246,7 +246,6 @@ function updateRemainingTime() { diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl index 6355a1b63..09617d1ea 100644 --- a/template/en/default/flag/list.html.tmpl +++ b/template/en/default/flag/list.html.tmpl @@ -68,6 +68,16 @@ #%]
- [% IF type.is_active %] @@ -73,12 +94,15 @@ - [% IF flag.status == "?" %] - [% IF flag.requestee %]([% flag.requestee.nick FILTER html %])[% END %] - [% ELSIF type.is_requestable && type.is_active %] + [% IF type.is_active && type.is_requestable %] - () + () [% END %]   [% type.name %] - @@ -100,8 +125,9 @@ - () + ()
addl. [% type.name %] - @@ -129,8 +156,9 @@ [% IF type.is_requestable && type.is_requesteeble %] - () + () [% END %] [% IF flag_types.size > 0 %] - Flags:
[% PROCESS "flag/list.html.tmpl" %] [% END %]
+ + + [% IF any_flags_requesteeble %] + + [% END %] + [%# Step 1: Display every flag type (except inactive types with no flags). %] [% FOREACH type = flag_types %] @@ -96,19 +106,21 @@ [% END %] - + [% IF any_flags_requesteeble %] + + [% END %] [% END %] @@ -116,7 +128,7 @@ [% IF (!type.flags || type.flags.size == 0) && type.is_active %] - + - + [% IF any_flags_requesteeble %] + + [% END %] [% END %] [% END %] @@ -149,7 +163,7 @@ [% separator_displayed = 1 %] [% END %] - + - + [% IF any_flags_requesteeble %] + + [% END %] [% END %] -- cgit v1.2.3-65-gdbad From 0fcc20760ab4c49ec7e3b93f9025a9b0b8d2046b Mon Sep 17 00:00:00 2001 From: "jake%bugzilla.org" <> Date: Thu, 28 Nov 2002 00:00:20 +0000 Subject: Bug 67077 - We now include the timezone (as configured in editparams.cgi) on every time we display. r=justdave a=justdave --- Attachment.pm | 10 +---- Bugzilla/Attachment.pm | 10 +---- Bugzilla/Util.pm | 51 +++++++++++++++++++++- CGI.pl | 2 +- attachment.cgi | 4 +- bug_form.pl | 14 +----- checksetup.pl | 1 + defparams.pl | 8 ++++ globals.pl | 9 ++-- request.cgi | 2 +- t/004template.t | 1 + t/007util.t | 17 +++++++- template/en/default/attachment/list.html.tmpl | 2 +- .../en/default/attachment/show-multiple.html.tmpl | 2 +- template/en/default/bug/activity/table.html.tmpl | 2 +- template/en/default/bug/comments.html.tmpl | 2 +- template/en/default/bug/edit.html.tmpl | 5 ++- template/en/default/request/queue.html.tmpl | 2 +- 18 files changed, 100 insertions(+), 44 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 53690170e..27d7fa2ca 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -69,8 +69,8 @@ sub query # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. &::SendSQL(" - SELECT attach_id, creation_ts, mimetype, description, ispatch, - isobsolete, isprivate, submitter_id + SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), + mimetype, description, ispatch, isobsolete, isprivate, submitter_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); @@ -81,12 +81,6 @@ sub query $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id) = &::FetchSQLData(); - # Format the attachment's creation/modification date into a standard - # format (YYYY-MM-DD HH:MM) - if ($a{'date'} =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { - $a{'date'} = "$1-$2-$3 $4:$5"; - } - # Retrieve a list of flags for this attachment. $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 53690170e..27d7fa2ca 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -69,8 +69,8 @@ sub query # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. &::SendSQL(" - SELECT attach_id, creation_ts, mimetype, description, ispatch, - isobsolete, isprivate, submitter_id + SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), + mimetype, description, ispatch, isobsolete, isprivate, submitter_id FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); my @attachments = (); @@ -81,12 +81,6 @@ sub query $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id) = &::FetchSQLData(); - # Format the attachment's creation/modification date into a standard - # format (YYYY-MM-DD HH:MM) - if ($a{'date'} =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { - $a{'date'} = "$1-$2-$3 $4:$5"; - } - # Retrieve a list of flags for this attachment. $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index f87c6fbc6..efd107c0a 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -25,11 +25,13 @@ package Bugzilla::Util; +use Bugzilla::Config; + use base qw(Exporter); @Bugzilla::Util::EXPORT = qw(is_tainted trick_taint detaint_natural html_quote url_quote value_quote lsearch max min - trim); + trim format_time); use strict; @@ -122,6 +124,36 @@ sub trim { return $str; } +# Bug 67077 +sub format_time { + my ($time) = @_; + + my ($year, $month, $day, $hour, $min); + if ($time =~ m/^\d{14}$/) { + # We appear to have a timestamp direct from MySQL + $year = substr($time,0,4); + $month = substr($time,4,2); + $day = substr($time,6,2); + $hour = substr($time,8,2); + $min = substr($time,10,2); + } + elsif ($time =~ m/^(\d{4})\.(\d{2})\.(\d{2}) (\d{2}):(\d{2})(:\d{2})?$/) { + $year = $1; + $month = $2; + $day = $3; + $hour = $4; + $min = $5; + } + else { + warn "Date/Time format ($time) unrecogonzied"; + } + + if (defined $year) { + $time = "$year-$month-$day $hour:$min " . &::Param('timezone'); + } + return $time; +} + 1; __END__ @@ -152,6 +184,9 @@ Bugzilla::Util - Generic utility functions for bugzilla # Functions for trimming variables $val = trim(" abc "); + # Functions for formatting time + format_time($time); + =head1 DESCRIPTION This package contains various utility functions which do not belong anywhere @@ -252,3 +287,17 @@ Removes any leading or trailing whitespace from a string. This routine does not modify the existing string. =back + +=head2 Formatting Time + +=over 4 + +=item C + +Takes a time and appends the timezone as defined in editparams.cgi. This routine +will be expanded in the future to adjust for user preferences regarding what +timezone to display times in. In the future, it may also allow for the time to be +shown in different formats. + +=back + diff --git a/CGI.pl b/CGI.pl index 24f92c072..ef1c16d85 100644 --- a/CGI.pl +++ b/CGI.pl @@ -812,7 +812,7 @@ sub GetBugActivity { SELECT IFNULL(fielddefs.description, bugs_activity.fieldid), fielddefs.name, bugs_activity.attach_id, - bugs_activity.bug_when, + DATE_FORMAT(bugs_activity.bug_when,'%Y.%m.%d %H:%i'), bugs_activity.removed, bugs_activity.added, profiles.login_name FROM bugs_activity LEFT JOIN fielddefs ON diff --git a/attachment.cgi b/attachment.cgi index 6e925e69a..27c2c107c 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -369,8 +369,8 @@ sub viewall if (Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { $privacy = "AND isprivate < 1 "; } - SendSQL("SELECT attach_id, creation_ts, mimetype, description, - ispatch, isobsolete, isprivate + SendSQL("SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), + mimetype, description, ispatch, isobsolete, isprivate FROM attachments WHERE bug_id = $::FORM{'bugid'} $privacy ORDER BY attach_id"); my @attachments; # the attachments array diff --git a/bug_form.pl b/bug_form.pl index e390ad51e..b4a2ef678 100644 --- a/bug_form.pl +++ b/bug_form.pl @@ -85,8 +85,7 @@ sub show_bug { 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), delta_ts calc_disp_date, + 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 @@ -111,19 +110,10 @@ sub show_bug { "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", "calc_disp_date", + "creation_ts", "delta_ts", "votes", "estimated_time", "remaining_time") { $value = shift(@row); - if ($field eq "calc_disp_date") { - # Convert MySQL timestamp (_ts) to datetime format(%Y-%m-%d %H:%i) - $disp_date = substr($value,0,4) . '-'; - $disp_date .= substr($value,4,2) . '-'; - $disp_date .= substr($value,6,2) . ' '; - $disp_date .= substr($value,8,2) . ':'; - $disp_date .= substr($value,10,2); - $value = $disp_date; - } $bug{$field} = defined($value) ? $value : ""; } diff --git a/checksetup.pl b/checksetup.pl index 5f947dabe..101583096 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -968,6 +968,7 @@ END html_linebreak => sub { return $_; }, url_quote => sub { return $_; }, csv => sub { return $_; }, + time => sub { return $_; }, }, }) || die ("Could not create Template Provider: " . Template::Provider->error() . "\n"); diff --git a/defparams.pl b/defparams.pl index ae0923295..3e4807dc2 100644 --- a/defparams.pl +++ b/defparams.pl @@ -230,6 +230,14 @@ sub check_netmask { default => '/' }, + { + name => 'timezone', + desc => 'The timezone that your SQL server lives in. If set to "" then' . + 'the timezone can\'t be displayed with the timestamps.', + type => 't', + default => '', + }, + { name => 'enablequips', desc => 'Controls the appearance of quips at the top of buglists.
    ' . diff --git a/globals.pl b/globals.pl index 07e9804c2..547fd1b95 100644 --- a/globals.pl +++ b/globals.pl @@ -1294,7 +1294,7 @@ sub GetLongDescriptionAsText { my $result = ""; my $count = 0; my $anyprivate = 0; - my ($query) = ("SELECT profiles.login_name, longdescs.bug_when, " . + my ($query) = ("SELECT profiles.login_name, DATE_FORMAT(longdescs.bug_when,'%Y.%d.%m %H:%i'), " . " longdescs.thetext, longdescs.isprivate " . "FROM longdescs, profiles " . "WHERE profiles.userid = longdescs.who " . @@ -1316,7 +1316,7 @@ sub GetLongDescriptionAsText { my ($who, $when, $text, $isprivate, $work_time) = (FetchSQLData()); if ($count) { $result .= "\n\n------- Additional Comments From $who".Param('emailsuffix')." ". - time2str("%Y-%m-%d %H:%M", str2time($when)) . " -------\n"; + Bugzilla::Util::format_time($when) . " -------\n"; } if (($isprivate > 0) && Param("insidergroup")) { $anyprivate = 1; @@ -1332,7 +1332,7 @@ sub GetComments { my ($id) = (@_); my @comments; SendSQL("SELECT profiles.realname, profiles.login_name, - date_format(longdescs.bug_when,'%Y-%m-%d %H:%i'), + date_format(longdescs.bug_when,'%Y.%m.%d %H:%i'), longdescs.thetext, longdescs.work_time, isprivate, date_format(longdescs.bug_when,'%Y%m%d%H%i%s') @@ -1793,6 +1793,9 @@ $::template ||= Template->new( } return $var; } , + + # Format a time for display (more info in Bugzilla::Util) + time => \&Bugzilla::Util::format_time, } , } ) || die("Template creation failed: " . Template->error()); diff --git a/request.cgi b/request.cgi index c74e97e64..920ac79cf 100755 --- a/request.cgi +++ b/request.cgi @@ -76,7 +76,7 @@ sub queue { flags.attach_id, attachments.description, requesters.realname, requesters.login_name, requestees.realname, requestees.login_name, - flags.creation_date, + DATE_FORMAT(flags.creation_date,'%Y.%m.%d %H:%i'), " . # Select columns that help us weed out secure bugs to which the user # should not have access. diff --git a/t/004template.t b/t/004template.t index bbcb9f860..6c44fca48 100644 --- a/t/004template.t +++ b/t/004template.t @@ -82,6 +82,7 @@ my $provider = Template::Provider->new( strike => sub { return $_ } , url_quote => sub { return $_ } , csv => sub { return $_ } , + time => sub { return $_ } , }, } ); diff --git a/t/007util.t b/t/007util.t index a683f407e..c32087d32 100644 --- a/t/007util.t +++ b/t/007util.t @@ -40,10 +40,19 @@ use lib 't'; use Support::Files; BEGIN { - use Test::More tests => 10; + use Test::More tests => 12; use_ok(Bugzilla::Util); } +# We need to override the the Param() function so we can get an expected +# value when Bugzilla::Utim::format_time calls asks for Param('timezone'). +# This will also prevent the tests from failing on site that do not have a +# data/params file containing Param('timezone') yet. +sub myParam { + return "TEST" if $_[0] eq 'timezone'; +} +*::Param = *myParam; + # we don't test the taint functions since that's going to take some more work. # XXX: test taint functions @@ -69,3 +78,9 @@ is(min(@list),2,'min()'); #trim(): is(trim(" fg<*\$%>+=~~ "),'fg<*$%>+=~~','trim()'); + +#format_time(); +is(format_time("20021123140436"),'2002-11-23 14:04 TEST','format_time("20021123140436")'); +is(format_time("2002.11.24 00:05:56"),'2002-11-24 00:05 TEST','format_time("2002.11.24 00:05:56")'); + + diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index 265803602..d64e65953 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -50,7 +50,7 @@ [% END %] -
+ [% IF show_attachment_flags %] - + [% FOREACH change = operation.changes %] [% "" IF loop.index > 0 %] diff --git a/template/en/default/bug/comments.html.tmpl b/template/en/default/bug/comments.html.tmpl index 6e97d9b1e..42971b327 100644 --- a/template/en/default/bug/comments.html.tmpl +++ b/template/en/default/bug/comments.html.tmpl @@ -44,7 +44,7 @@ ------- Additional Comment #[% count %] From [% comment.name FILTER html %] - [%+ comment.time %] + [%+ comment.time FILTER time %] ------- [% END %] diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index 4af65af1c..aa56678dd 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -21,12 +21,13 @@ #%] [% 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: $bug.calc_disp_date" + h3 = "Last modified: $filtered_timestamp" style_urls = [ "css/edit_bug.css" ] %] [% END %] @@ -590,7 +591,7 @@
+ Flags: + + Requestee: +
- [% IF type.is_active && type.is_requesteeble %] - - () - - [% END %] - + [% IF type.is_active && type.is_requesteeble %] + + () + + [% END %] +
 [% type.name %][% type.name FILTER html %] - [% IF type.is_requesteeble %] - - () - - [% END %] - + [% IF type.is_requesteeble %] + + () + + [% END %] +
addl. [% type.name %]addl. [% type.name FILTER html %] - [% IF type.is_requesteeble %] - - () - - [% END %] - + [% IF type.is_requesteeble %] + + () + + [% END %] +
[% attachment.date %][% attachment.date FILTER time %] diff --git a/template/en/default/attachment/show-multiple.html.tmpl b/template/en/default/attachment/show-multiple.html.tmpl index 5e12f622e..53149085a 100644 --- a/template/en/default/attachment/show-multiple.html.tmpl +++ b/template/en/default/attachment/show-multiple.html.tmpl @@ -58,7 +58,7 @@ [% END %] [% a.date %][% a.date FILTER time %] [% IF a.statuses.size == 0 %] diff --git a/template/en/default/bug/activity/table.html.tmpl b/template/en/default/bug/activity/table.html.tmpl index 45c8e4380..95beaef14 100644 --- a/template/en/default/bug/activity/table.html.tmpl +++ b/template/en/default/bug/activity/table.html.tmpl @@ -61,7 +61,7 @@ [% operation.who %] - [% operation.when %] + [% operation.when FILTER time %]
- Opened: [% bug.creation_ts %] + Opened: [% bug.creation_ts FILTER time %]
diff --git a/template/en/default/request/queue.html.tmpl b/template/en/default/request/queue.html.tmpl index fcf30ee6d..19e2cbe54 100644 --- a/template/en/default/request/queue.html.tmpl +++ b/template/en/default/request/queue.html.tmpl @@ -200,6 +200,6 @@ [% END %] [% BLOCK display_created %] - [% request.created FILTER html %] + [% request.created FILTER time %] [% END %] -- cgit v1.2.3-65-gdbad From 6bd37cce67502e54410dde53f615b5d9b860a4be Mon Sep 17 00:00:00 2001 From: "bugreport%peshkin.net" <> Date: Wed, 11 Dec 2002 08:41:19 +0000 Subject: Bug 184256 Canedit group_control_map entry does not prevent making attachments r=bbaetz a=justdave --- Attachment.pm | 9 +++++++-- Bugzilla/Attachment.pm | 9 +++++++-- attachment.cgi | 26 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 27d7fa2ca..322a3b2ba 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -65,6 +65,11 @@ sub query my ($bugid) = @_; my $in_editbugs = &::UserInGroup("editbugs"); + &::SendSQL("SELECT product_id + FROM bugs + WHERE bug_id = $bugid"); + my $productid = &::FetchOneColumn(); + my $caneditproduct = &::CanEditProductId($productid); # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. @@ -88,8 +93,8 @@ sub query # ie the are the submitter, or they have canedit. # Also show the link if the user is not logged in - in that cae, # They'll be prompted later - $a{'canedit'} = ($::userid == 0 || $submitter_id == $::userid || - $in_editbugs); + $a{'canedit'} = ($::userid == 0 || (($submitter_id == $::userid || + $in_editbugs) && $caneditproduct)); push @attachments, \%a; } diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 27d7fa2ca..322a3b2ba 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -65,6 +65,11 @@ sub query my ($bugid) = @_; my $in_editbugs = &::UserInGroup("editbugs"); + &::SendSQL("SELECT product_id + FROM bugs + WHERE bug_id = $bugid"); + my $productid = &::FetchOneColumn(); + my $caneditproduct = &::CanEditProductId($productid); # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. @@ -88,8 +93,8 @@ sub query # ie the are the submitter, or they have canedit. # Also show the link if the user is not logged in - in that cae, # They'll be prompted later - $a{'canedit'} = ($::userid == 0 || $submitter_id == $::userid || - $in_editbugs); + $a{'canedit'} = ($::userid == 0 || (($submitter_id == $::userid || + $in_editbugs) && $caneditproduct)); push @attachments, \%a; } diff --git a/attachment.cgi b/attachment.cgi index 27c2c107c..5c3ce09ac 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -77,12 +77,14 @@ elsif ($action eq "enter") { confirm_login(); ValidateBugID($::FORM{'bugid'}); + validateCanChangeBug($::FORM{'bugid'}); enter(); } elsif ($action eq "insert") { confirm_login(); ValidateBugID($::FORM{'bugid'}); + validateCanChangeBug($::FORM{'bugid'}); ValidateComment($::FORM{'comment'}); validateFilename(); validateIsPatch(); @@ -105,6 +107,7 @@ elsif ($action eq "update") ValidateComment($::FORM{'comment'}); validateID(); validateCanEdit($::FORM{'id'}); + validateCanChangeAttachment($::FORM{'id'}); validateDescription(); validateIsPatch(); validateContentType() unless $::FORM{'ispatch'}; @@ -171,6 +174,29 @@ sub validateCanEdit || ThrowUserError("illegal_attachment_edit"); } +sub validateCanChangeAttachment +{ + my ($attachid) = @_; + SendSQL("SELECT product_id + FROM attachments, bugs + WHERE attach_id = $attachid + AND bugs.bug_id = attachments.bug_id"); + my $productid = FetchOneColumn(); + CanEditProductId($productid) + || ThrowUserError("illegal_attachment_edit"); +} + +sub validateCanChangeBug +{ + my ($bugid) = @_; + SendSQL("SELECT product_id + FROM bugs + WHERE bug_id = $bugid"); + my $productid = FetchOneColumn(); + CanEditProductId($productid) + || ThrowUserError("illegal_attachment_edit"); +} + sub validateDescription { $::FORM{'description'} -- cgit v1.2.3-65-gdbad From ee84183ca9efa0839c49ad02f293d60db0a4d76f Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Sun, 15 Dec 2002 17:01:12 +0000 Subject: Bug 116819 - Attach and Reassign in one fell swoop. Patch by gerv; r,a=justdave. --- attachment.cgi | 68 +++++++++++++++++++++---- template/en/default/attachment/create.html.tmpl | 11 ++++ 2 files changed, 70 insertions(+), 9 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 5c3ce09ac..17e237564 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -416,14 +416,16 @@ sub viewall push @attachments, \%a; } - # Retrieve the bug summary for displaying on screen. - SendSQL("SELECT short_desc FROM bugs WHERE bug_id = $::FORM{'bugid'}"); - my ($bugsummary) = FetchSQLData(); + # Retrieve the bug summary (for displaying on screen) and assignee. + SendSQL("SELECT short_desc, assigned_to FROM bugs " . + "WHERE bug_id = $::FORM{'bugid'}"); + my ($bugsummary, $assignee_id) = FetchSQLData(); # Define the variables and functions that will be passed to the UI template. $vars->{'bugid'} = $::FORM{'bugid'}; - $vars->{'bugsummary'} = $bugsummary; $vars->{'attachments'} = \@attachments; + $vars->{'bugassignee_id'} = $assignee_id; + $vars->{'bugsummary'} = $bugsummary; # Return the appropriate HTTP response headers. print "Content-Type: text/html\n\n"; @@ -458,14 +460,16 @@ sub enter push @attachments, \%a; } - # Retrieve the bug summary for displaying on screen. - SendSQL("SELECT short_desc FROM bugs WHERE bug_id = $::FORM{'bugid'}"); - my ($bugsummary) = FetchSQLData(); + # Retrieve the bug summary (for displaying on screen) and assignee. + SendSQL("SELECT short_desc, assigned_to FROM bugs + WHERE bug_id = $::FORM{'bugid'}"); + my ($bugsummary, $assignee_id) = FetchSQLData(); # Define the variables and functions that will be passed to the UI template. $vars->{'bugid'} = $::FORM{'bugid'}; - $vars->{'bugsummary'} = $bugsummary; $vars->{'attachments'} = \@attachments; + $vars->{'bugassignee_id'} = $assignee_id; + $vars->{'bugsummary'} = $bugsummary; # Return the appropriate HTTP response headers. print "Content-Type: text/html\n\n"; @@ -523,6 +527,51 @@ sub insert } } + # Assign the bug to the user, if they are allowed to take it + my $forcecc = ""; + + if ($::FORM{'takebug'} && UserInGroup("editbugs")) { + SendSQL("select NOW()"); + my $timestamp = FetchOneColumn(); + + my @fields = ("assigned_to", "bug_status", "resolution", "login_name"); + + # Get the old values, for the bugs_activity table + SendSQL("SELECT " . join(", ", @fields) . " FROM bugs, profiles " . + "WHERE bugs.bug_id = $::FORM{'bugid'} " . + "AND profiles.userid = bugs.assigned_to"); + + my @oldvalues = FetchSQLData(); + my @newvalues = ($::userid, "ASSIGNED", "", DBID_to_name($::userid)); + + # Make sure the person we are taking the bug from gets mail. + $forcecc = $oldvalues[3]; + + @oldvalues = map(SqlQuote($_), @oldvalues); + @newvalues = map(SqlQuote($_), @newvalues); + + # Update the bug record. Note that this doesn't involve login_name. + SendSQL("UPDATE bugs SET " . + join(", ", map("$fields[$_] = $newvalues[$_]", (0..2))) . + " WHERE bug_id = $::FORM{'bugid'}"); + + # We store email addresses in the bugs_activity table rather than IDs. + $oldvalues[0] = $oldvalues[3]; + $newvalues[0] = $newvalues[3]; + + # Add the changes to the bugs_activity table + for (my $i = 0; $i < 3; $i++) { + if ($oldvalues[$i] ne $newvalues[$i]) { + my $fieldid = GetFieldID($fields[$i]); + SendSQL("INSERT INTO bugs_activity " . + "(bug_id, who, bug_when, fieldid, removed, added) " . + " VALUES ($::FORM{'bugid'}, $::userid, " . + SqlQuote($timestamp) . + ", $fieldid, $oldvalues[$i], $newvalues[$i])"); + } + } + } + # Send mail to let people know the attachment has been created. Uses a # special syntax of the "open" and "exec" commands to capture the output of # "processmail", which "system" doesn't allow, without running the command @@ -530,7 +579,8 @@ sub insert #system ("./processmail", $bugid , $::userid); #my $mailresults = `./processmail $bugid $::userid`; my $mailresults = ''; - open(PMAIL, "-|") or exec('./processmail', $::FORM{'bugid'}, $::COOKIE{'Bugzilla_login'}); + open(PMAIL, "-|") or exec('./processmail', '-forcecc', $forcecc, + $::FORM{'bugid'}, $::COOKIE{'Bugzilla_login'}); $mailresults .= $_ while ; close(PMAIL); diff --git a/template/en/default/attachment/create.html.tmpl b/template/en/default/attachment/create.html.tmpl index 1c00146e5..f7cfe21f9 100644 --- a/template/en/default/attachment/create.html.tmpl +++ b/template/en/default/attachment/create.html.tmpl @@ -122,6 +122,17 @@ [% END %]
Reassignment: + If you want to assign this bug to yourself, + check the box below.
+ + +
Comment: -- cgit v1.2.3-65-gdbad From f50d2365975b419d62b398af9a886da4f5bcaff4 Mon Sep 17 00:00:00 2001 From: "jouni%heikniemi.net" <> Date: Tue, 14 Jan 2003 14:36:09 +0000 Subject: Bug 156169: Bug number styling issues in attachment viewer/editor. r=joel, a=justdave --- attachment.cgi | 3 +++ template/en/default/attachment/create.html.tmpl | 3 ++- template/en/default/attachment/edit.html.tmpl | 3 ++- template/en/default/attachment/show-multiple.html.tmpl | 4 +++- 4 files changed, 10 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 17e237564..bb166e2f8 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -426,6 +426,7 @@ sub viewall $vars->{'attachments'} = \@attachments; $vars->{'bugassignee_id'} = $assignee_id; $vars->{'bugsummary'} = $bugsummary; + $vars->{'GetBugLink'} = \&GetBugLink; # Return the appropriate HTTP response headers. print "Content-Type: text/html\n\n"; @@ -470,6 +471,7 @@ sub enter $vars->{'attachments'} = \@attachments; $vars->{'bugassignee_id'} = $assignee_id; $vars->{'bugsummary'} = $bugsummary; + $vars->{'GetBugLink'} = \&GetBugLink; # Return the appropriate HTTP response headers. print "Content-Type: text/html\n\n"; @@ -653,6 +655,7 @@ sub edit $vars->{'isprivate'} = $isprivate; $vars->{'isviewable'} = $isviewable; $vars->{'attachments'} = \@bugattachments; + $vars->{'GetBugLink'} = \&GetBugLink; # Return the appropriate HTTP response headers. print "Content-Type: text/html\n\n"; diff --git a/template/en/default/attachment/create.html.tmpl b/template/en/default/attachment/create.html.tmpl index f7cfe21f9..a298df5a9 100644 --- a/template/en/default/attachment/create.html.tmpl +++ b/template/en/default/attachment/create.html.tmpl @@ -21,7 +21,8 @@ [%# Define strings that will serve as the title and header of this page %] [% title = BLOCK %]Create New Attachment for Bug #[% bugid %][% END %] -[% h1 = BLOCK %]Create New Attachment for Bug #[% bugid %][% END %] +[% h1 = BLOCK %]Create New Attachment for + [%+ GetBugLink(bugid, "Bug $bugid") %][% END %] [% h2 = BLOCK %][% bugsummary FILTER html %][% END %] [% PROCESS global/header.html.tmpl diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 8cd92774b..57d99f766 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -21,7 +21,8 @@ [%# Define strings that will serve as the title and header of this page %] [% title = BLOCK %]Edit Attachment #[% attachid %] for Bug #[% bugid %][% END %] -[% h1 = BLOCK %]Edit Attachment #[% attachid %] for Bug #[% bugid %][% END %] +[% h1 = BLOCK %]Edit Attachment #[% attachid %] for + [%+ GetBugLink(bugid, "Bug $bugid") %][% END %] [% h2 = BLOCK %][% bugsummary FILTER html %][% END %] [% PROCESS global/header.html.tmpl diff --git a/template/en/default/attachment/show-multiple.html.tmpl b/template/en/default/attachment/show-multiple.html.tmpl index 53149085a..b137504e5 100644 --- a/template/en/default/attachment/show-multiple.html.tmpl +++ b/template/en/default/attachment/show-multiple.html.tmpl @@ -20,9 +20,11 @@ #%] [% filtered_summary = bugsummary FILTER html %] +[% h1 = BLOCK %]View All Attachments for + [%+ GetBugLink(bugid, "Bug $bugid") %][% END %] [% PROCESS global/header.html.tmpl title = "View All Attachments for Bug #$bugid" - h1 = "View All Attachments for Bug #$bugid" + h1 = h1 h2 = filtered_summary style = " table.attachment_info th { text-align: right; vertical-align: top; } -- cgit v1.2.3-65-gdbad From 4a8e3d64a5af8ae7a82cdb7bbbc39afbf38184b4 Mon Sep 17 00:00:00 2001 From: "preed%sigkill.com" <> Date: Mon, 10 Feb 2003 06:04:08 +0000 Subject: Bug 124174 - make processmail a package (Bugzilla::BugMail), r=gerv, r=jth, a=justdave --- Bugzilla/BugMail.pm | 879 ++++++++++++++++++++ Bugzilla/Template.pm | 7 + CGI.pl | 5 +- attachment.cgi | 27 +- post_bug.cgi | 37 +- process_bug.cgi | 48 +- processmail | 928 ---------------------- sanitycheck.cgi | 27 +- template/en/default/attachment/created.html.tmpl | 3 +- template/en/default/attachment/updated.html.tmpl | 3 +- template/en/default/bug/process/bugmail.html.tmpl | 70 ++ template/en/default/bug/process/results.html.tmpl | 4 +- 12 files changed, 1008 insertions(+), 1030 deletions(-) create mode 100644 Bugzilla/BugMail.pm delete mode 100755 processmail create mode 100644 template/en/default/bug/process/bugmail.html.tmpl (limited to 'attachment.cgi') diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm new file mode 100644 index 000000000..a71effcd2 --- /dev/null +++ b/Bugzilla/BugMail.pm @@ -0,0 +1,879 @@ +# -*- 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 , +# Bryce Nesbitt +# Dan Mosedale +# Alan Raetz +# Jacob Steenhagen +# Matthew Tuck +# Bradley Baetz +# J. Paul Reed + +use strict; + +package Bugzilla::BugMail; + +use RelationSet; + +# This code is really ugly. It was a commandline interface, then it was moved +# There are package-global variables which we rely on ProcessOneBug to clean +# up each time, and other sorts of fun. +# This really needs to be cleaned at some point. + +my $nametoexclude = ""; +my %nomail; +my $last_changed; + +my @excludedAddresses = (); + +# disable email flag for offline debugging work +my $enableSendMail = 1; + +my %force; + +my %seen; +my @sentlist; + +# I got sick of adding &:: to everything. +# However, 'Yuck!' +# I can't require, cause that pulls it in only once, so it won't then be +# in the global package, and these aren't modules, so I can't use globals.pl +# Remove this evilness once our stuff uses real packages. +sub AUTOLOAD { + no strict 'refs'; + use vars qw($AUTOLOAD); + my $subName = $AUTOLOAD; + $subName =~ s/.*::/::/; # remove package name + *$AUTOLOAD = \&$subName; + goto &$AUTOLOAD; +} + +# This is run when we load the package +if (open(NOMAIL, ") { + $nomail{trim($_)} = 1; + } + close(NOMAIL); +} + + +sub FormatTriple { + my ($a, $b, $c) = (@_); + $^A = ""; + my $temp = formline << 'END', $a, $b, $c; +^>>>>>>>>>>>>>>>>>>|^<<<<<<<<<<<<<<<<<<<<<<<<<<<|^<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ +END + ; # This semicolon appeases my emacs editor macros. :-) + return $^A; +} + +sub FormatDouble { + my ($a, $b) = (@_); + $a .= ":"; + $^A = ""; + my $temp = formline << 'END', $a, $b; +^>>>>>>>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ +END + ; # This semicolon appeases my emacs editor macros. :-) + return $^A; +} + +# This is a bit of a hack, basically keeping the old system() +# cmd line interface. Should clean this up at some point. +# +# args: bug_id, and an optional hash ref which may have keys for: +# changer, owner, qa, reporter, cc +# Optional hash contains values of people which will be forced to those +# roles when the email is sent. +# All the names are email addresses, not userids +# values are scalars, except for cc, which is a list +sub Send($;$) { + my ($id, $recipients) = (@_); + + # This doesn't work if its not in a sub. Probably something to do with the + # require abuse we do. + GetVersionTable(); + + # Since any email recipients must be rederived if the user has not + # been rederived since the most recent group change, figure out when that + # is once and determine the need to rederive users using the same DB + # access that gets the user's email address each time a person is + # processed. + SendSQL("SELECT MAX(last_changed) FROM groups"); + ($last_changed) = FetchSQLData(); + + # Make sure to clean up _all_ package vars here. Yuck... + $nametoexclude = $recipients->{'changer'} || ""; + @{$force{'CClist'}} = (exists $recipients->{'cc'} && + scalar($recipients->{'cc'}) > 0) ? map(trim($_), + @{$recipients->{'cc'}}) : (); + @{$force{'Owner'}} = $recipients->{'owner'} ? + (trim($recipients->{'owner'})) : (); + @{$force{'QAcontact'}} = $recipients->{'qacontact'} ? + (trim($recipients->{'qacontact'})) : (); + @{$force{'Reporter'}} = $recipients->{'reporter'} ? + (trim($recipients->{'reporter'})) : (); + @{$force{'Voter'}} = (); + + %seen = (); + @excludedAddresses = (); + @sentlist = (); + + return ProcessOneBug($id); +} + +sub ProcessOneBug($) { + my ($id) = (@_); + + my @headerlist; + my %values; + my %defmailhead; + my %fielddescription; + + my $msg = ""; + + SendSQL("SELECT name, description, mailhead FROM fielddefs " . + "ORDER BY sortkey"); + while (MoreSQLData()) { + my ($field, $description, $mailhead) = (FetchSQLData()); + push(@headerlist, $field); + $defmailhead{$field} = $mailhead; + $fielddescription{$field} = $description; + } + SendSQL("SELECT " . join(',', @::log_columns) . ", lastdiffed, now() " . + "FROM bugs WHERE bug_id = $id"); + my @row = FetchSQLData(); + foreach my $i (@::log_columns) { + $values{$i} = shift(@row); + } + $values{product} = get_product_name($values{product_id}); + $values{component} = get_component_name($values{component_id}); + + my ($start, $end) = (@row); + # $start and $end are considered safe because users can't touch them + trick_taint($start); + trick_taint($end); + + my $ccSet = new RelationSet(); + $ccSet->mergeFromDB("SELECT who FROM cc WHERE bug_id = $id"); + $values{'cc'} = $ccSet->toString(); + + my @voterList; + SendSQL("SELECT profiles.login_name FROM votes, profiles " . + "WHERE votes.bug_id = $id AND profiles.userid = votes.who"); + while (MoreSQLData()) { + push(@voterList, FetchOneColumn()); + } + + $values{'assigned_to'} = DBID_to_name($values{'assigned_to'}); + $values{'reporter'} = DBID_to_name($values{'reporter'}); + if ($values{'qa_contact'}) { + $values{'qa_contact'} = DBID_to_name($values{'qa_contact'}); + } + $values{'estimated_time'} = FormatTimeUnit($values{'estimated_time'}); + + my @dependslist; + SendSQL("SELECT dependson FROM dependencies WHERE + blocked = $id ORDER BY dependson"); + while (MoreSQLData()) { + push(@dependslist, FetchOneColumn()); + } + $values{'dependson'} = join(",", @dependslist); + + my @blockedlist; + SendSQL("SELECT blocked FROM dependencies WHERE + dependson = $id ORDER BY blocked"); + while (MoreSQLData()) { + push(@blockedlist, FetchOneColumn()); + } + $values{'blocked'} = join(",", @blockedlist); + + my @diffs; + + + SendSQL("SELECT profiles.login_name, fielddefs.description, " . + " bug_when, removed, added, attach_id, fielddefs.name " . + "FROM bugs_activity, fielddefs, profiles " . + "WHERE bug_id = $id " . + " AND fielddefs.fieldid = bugs_activity.fieldid " . + " AND profiles.userid = who " . + " AND bug_when > '$start' " . + " AND bug_when <= '$end' " . + "ORDER BY bug_when" + ); + + while (MoreSQLData()) { + my @row = FetchSQLData(); + push(@diffs, \@row); + } + + my $difftext = ""; + my $diffheader = ""; + my $diffpart = {}; + my @diffparts; + my $lastwho = ""; + foreach my $ref (@diffs) { + my ($who, $what, $when, $old, $new, $attachid, $fieldname) = (@$ref); + $diffpart = {}; + if ($who ne $lastwho) { + $lastwho = $who; + $diffheader = "\n$who" . Param('emailsuffix') . " changed:\n\n"; + $diffheader .= FormatTriple("What ", "Removed", "Added"); + $diffheader .= ('-' x 76) . "\n"; + } + $what =~ s/^(Attachment )?/Attachment #$attachid / if $attachid; + if( $fieldname eq 'estimated_time' || + $fieldname eq 'remaining_time' ) { + $old = FormatTimeUnit($old); + $new = FormatTimeUnit($new); + } + $difftext = FormatTriple($what, $old, $new); + $diffpart->{'header'} = $diffheader; + $diffpart->{'fieldname'} = $fieldname; + $diffpart->{'text'} = $difftext; + push(@diffparts, $diffpart); + } + + + my $deptext = ""; + + my $resid = + + SendSQL("SELECT bugs_activity.bug_id, bugs.short_desc, fielddefs.name, " . + " removed, added " . + "FROM bugs_activity, bugs, dependencies, fielddefs ". + "WHERE bugs_activity.bug_id = dependencies.dependson " . + " AND bugs.bug_id = bugs_activity.bug_id ". + " AND dependencies.blocked = $id " . + " AND fielddefs.fieldid = bugs_activity.fieldid" . + " AND (fielddefs.name = 'bug_status' " . + " OR fielddefs.name = 'resolution') " . + " AND bug_when > '$start' " . + " AND bug_when <= '$end' " . + "ORDER BY bug_when, bug_id"); + + my $thisdiff = ""; + my $lastbug = ""; + my $interestingchange = 0; + my $depbug = 0; + my @depbugs; + while (MoreSQLData()) { + my ($summary, $what, $old, $new); + ($depbug, $summary, $what, $old, $new) = (FetchSQLData()); + if ($depbug ne $lastbug) { + if ($interestingchange) { + $deptext .= $thisdiff; + } + $lastbug = $depbug; + my $urlbase = Param("urlbase"); + $thisdiff = + "\nBug $id depends on bug $depbug, which changed state.\n\n" . + "Bug $depbug Summary: $summary\n" . + "${urlbase}show_bug.cgi?id=$depbug\n\n"; + $thisdiff .= FormatTriple("What ", "Old Value", "New Value"); + $thisdiff .= ('-' x 76) . "\n"; + $interestingchange = 0; + } + $thisdiff .= FormatTriple($fielddescription{$what}, $old, $new); + if ($what eq 'bug_status' && IsOpenedState($old) ne IsOpenedState($new)) { + $interestingchange = 1; + } + + push(@depbugs, $depbug); + } + + if ($interestingchange) { + $deptext .= $thisdiff; + } + + $deptext = trim($deptext); + + if ($deptext) { + #$difftext = trim($difftext . "\n\n" . $deptext); + $diffpart->{'text'} = trim("\n\n" . $deptext); + push(@diffparts, $diffpart); + } + + + my ($newcomments, $anyprivate) = GetLongDescriptionAsText($id, $start, $end); + + # + # Start of email filtering code + # + my $count = 0; + + # Get a list of the reasons a user might receive email about the bug. + my @currentEmailAttributes = + getEmailAttributes(\%values, \@diffs, $newcomments); + + my (@assigned_toList,@reporterList,@qa_contactList,@ccList) = (); + + #open(LOG, ">>/tmp/maillog"); + #print LOG "\nBug ID: $id CurrentEmailAttributes:"; + #print LOG join(',', @currentEmailAttributes) . "\n"; + + @excludedAddresses = (); # zero out global list + + @assigned_toList = filterEmailGroup('Owner', + \@currentEmailAttributes, + $values{'assigned_to'}); + @reporterList = filterEmailGroup('Reporter', + \@currentEmailAttributes, + $values{'reporter'}); + if (Param('useqacontact') && $values{'qa_contact'}) { + @qa_contactList = filterEmailGroup('QAcontact', + \@currentEmailAttributes, + $values{'qa_contact'}); + } else { + @qa_contactList = (); + } + + @ccList = filterEmailGroup('CClist', \@currentEmailAttributes, + $values{'cc'}); + + @voterList = filterEmailGroup('Voter', \@currentEmailAttributes, + join(',',@voterList)); + + my @emailList = (@assigned_toList, @reporterList, + @qa_contactList, @ccList, @voterList); + + # only need one entry per person + my @allEmail = (); + my %AlreadySeen = (); + my $checkperson = ""; + foreach my $person (@emailList) { + # don't modify the original so it sends out with the right case + # based on who came first. + $checkperson = lc($person); + if ( !($AlreadySeen{$checkperson}) ) { + push(@allEmail,$person); + $AlreadySeen{$checkperson}++; + } + } + + #print LOG "\nbug $id email sent: " . join(',', @allEmail) . "\n"; + + @excludedAddresses = filterExcludeList(\@excludedAddresses, + \@allEmail); + + # print LOG "excluded: " . join(',',@excludedAddresses) . "\n\n"; + + foreach my $person ( @allEmail ) { + my @reasons; + + $count++; + + push(@reasons, 'AssignedTo') if lsearch(\@assigned_toList, $person) != -1; + push(@reasons, 'Reporter') if lsearch(\@reporterList, $person) != -1; + push(@reasons, 'QAcontact') if lsearch(\@qa_contactList, $person) != -1; + push(@reasons, 'CC') if lsearch(\@ccList, $person) != -1; + push(@reasons, 'Voter') if lsearch(\@voterList, $person) != -1; + + if ( !defined(NewProcessOnePerson($person, $count, \@headerlist, + \@reasons, \%values, + \%defmailhead, + \%fielddescription, \@diffparts, + $newcomments, + $anyprivate, $start, $id, + \@depbugs))) + { + + # if a value is not returned, this means that the person + # was not sent mail. add them to the excludedAddresses list. + # it will be filtered later for dups. + # + push @excludedAddresses, $person; + + } + } + + + SendSQL("UPDATE bugs SET lastdiffed = '$end', delta_ts = delta_ts " . + "WHERE bug_id = $id"); + + # Filter the exclude list for dupes one last time + @excludedAddresses = filterExcludeList(\@excludedAddresses, + \@sentlist); + + return { sent => \@sentlist, excluded => \@excludedAddresses }; +} + +# When one person is in different fields on one bug, they may be +# excluded from email because of one relationship to the bug (eg +# they're the QA contact) but included because of another (eg they +# also reported the bug). Inclusion takes precedence, so this +# function looks for and removes any users from the exclude list who +# are also on the include list. Additionally, it removes duplicate +# entries from the exclude list. +# +# Arguments are the exclude list and the include list; the cleaned up +# exclude list is returned. +# +sub filterExcludeList ($$) { + + if ($#_ != 1) { + die ("filterExcludeList called with wrong number of args"); + } + + my ($refExcluded, $refAll) = @_; + + my @excludedAddrs = @$refExcluded; + my @allEmail = @$refAll; + my @tmpList = @excludedAddrs; + my (@result,@uniqueResult) = (); + my %alreadySeen; + + foreach my $excluded (@tmpList) { + + push (@result,$excluded); + foreach my $included (@allEmail) { + + # match found, so we remove the entry + if (lc($included) eq lc($excluded)) { + pop(@result); + last; + } + } + } + + # only need one entry per person + my $checkperson = ""; + + foreach my $person (@result) { + $checkperson = lc($person); + if ( !($alreadySeen{$checkperson}) ) { + push(@uniqueResult,$person); + $alreadySeen{$checkperson}++; + } + } + + return @uniqueResult; +} + +# if the Status was changed to Resolved or Verified +# set the Resolved flag +# +# else if Severity, Status, Target Milestone OR Priority fields have any change +# set the Status flag +# +# else if Keywords has changed +# set the Keywords flag +# +# else if CC has changed +# set the CC flag +# +# if the Comments field shows an attachment +# set the Attachment flag +# +# else if Comments exist +# set the Comments flag +# +# if no flags are set and there was some other field change +# set the Status flag +# +sub getEmailAttributes (\%\@$) { + + my ($bug, $fieldDiffs, $commentField) = @_; + my (@flags,@uniqueFlags,%alreadySeen) = (); + + # Add a flag if the status of the bug is "unconfirmed". + if ($bug->{'bug_status'} eq $::unconfirmedstate) { + push (@flags, 'Unconfirmed') + }; + + foreach my $ref (@$fieldDiffs) { + my ($who, $fieldName, $when, $old, $new) = (@$ref); + + #print qq{field: $fieldName $new
}; + + # the STATUS will be flagged for Severity, Status, Target Milestone and + # Priority changes + # + if ( $fieldName eq 'Status' && ($new eq 'RESOLVED' || $new eq 'VERIFIED')) { + push (@flags, 'Resolved'); + } + elsif ( $fieldName eq 'Severity' || $fieldName eq 'Status' || + $fieldName eq 'Priority' || $fieldName eq 'Target Milestone') { + push (@flags, 'Status'); + } elsif ( $fieldName eq 'Keywords') { + push (@flags, 'Keywords'); + } elsif ( $fieldName eq 'CC') { + push (@flags, 'CC'); + } + + # These next few lines are for finding out who's been added + # to the Owner, QA, CC, etc. fields. It does not effect + # the @flags array at all, but is run here because it does + # effect filtering later and we're already in the loop. + if ($fieldName eq 'AssignedTo') { + push (@{$force{'Owner'}}, $new); + } elsif ($fieldName eq 'QAcontact') { + push (@{$force{'QAcontact'}}, $new); + } elsif ($fieldName eq 'CC') { + my @added = split (/[ ,]/, $new); + push (@{$force{'CClist'}}, @added); + } + } + + if ( $commentField =~ /Created an attachment \(/ ) { + push (@flags, 'Attachments'); + } + elsif ( ($commentField ne '') && !(scalar(@flags) == 1 && $flags[0] eq 'Resolved')) { + push (@flags, 'Comments'); + } + + # default fallthrough for any unflagged change is 'Other' + if ( @flags == 0 && @$fieldDiffs != 0 ) { + push (@flags, 'Other'); + } + + # only need one flag per attribute type + foreach my $flag (@flags) { + if ( !($alreadySeen{$flag}) ) { + push(@uniqueFlags,$flag); + $alreadySeen{$flag}++; + } + } + #print "\nEmail Attributes: ", join(' ,',@uniqueFlags), "
\n"; + + # catch-all default, just in case the above logic is faulty + if ( @uniqueFlags == 0) { + push (@uniqueFlags, 'Comments'); + } + + return @uniqueFlags; +} + +sub filterEmailGroup ($$$) { + # This function figures out who should receive email about the bug + # based on the user's role with regard to the bug (assignee, reporter + # etc.), the changes that occurred (new comments, attachment added, + # status changed etc.) and the user's email preferences. + + # Returns a filtered list of those users who would receive email + # about these changes, and adds the names of those users who would + # not receive email about them to the global @excludedAddresses list. + + my ($role, $reasons, $users) = @_; + + # Make a list of users to filter. + my @users = split( /,/ , $users ); + + # Treat users who are in the process of being removed from this role + # as if they were still in it. + push @users, @{$force{$role}}; + + # If this installation supports user watching, add to the list those + # users who are watching other users already on the list. By doing + # this here, we allow watchers to inherit the roles of the people + # they are watching while at the same time filtering email for watchers + # based on their own preferences. + if (Param("supportwatchers") && scalar(@users)) { + # Convert the unfiltered list of users into an SQL-quoted, + # comma-separated string suitable for use in an SQL query. + my $watched = join(",", map(SqlQuote($_), @users)); + SendSQL("SELECT watchers.login_name + FROM watch, profiles AS watchers, profiles AS watched + WHERE watched.login_name IN ($watched) + AND watched.userid = watch.watched + AND watch.watcher = watchers.userid"); + push (@users, FetchOneColumn()) while MoreSQLData(); + } + + # Initialize the list of recipients. + my @recipients = (); + + USER: foreach my $user (@users) { + next unless $user; + + # Get the user's unique ID, and if the user is not registered + # (no idea why unregistered users should even be on this list, + # but the code that was here before I re-wrote it allows this), + # then we do not have any preferences for them, so assume the + # default preference to receive all mail for any reason. + my $userid = DBname_to_id($user); + if (!$userid) { + push(@recipients, $user); + next; + } + + # Get the user's email preferences from the database. + SendSQL("SELECT emailflags FROM profiles WHERE userid = $userid"); + my $prefs = FetchOneColumn(); + + # If the user's preferences are empty, assume the default preference + # to receive all mail. This happens when the installation upgraded + # from a version of Bugzilla without email preferences to one with + # them, but the user has not set their preferences yet. + if (!defined($prefs) || $prefs !~ /email/) { + push(@recipients, $user); + next; + } + + # Write the user's preferences into a Perl record indexed by + # preference name. We pass the value "255" to the split function + # because otherwise split will trim trailing null fields, causing + # Perl to generate lots of warnings. Any suitably large number + # would do. + my %prefs = split(/~/, $prefs, 255); + + # If this user is the one who made the change in the first place, + # and they prefer not to receive mail about their own changes, + # filter them from the list. + if (lc($user) eq $nametoexclude && $prefs{'ExcludeSelf'} eq 'on') { + push(@excludedAddresses, $user); + next; + } + + # If the user doesn't want to receive email about unconfirmed + # bugs, that setting overrides their other preferences, including + # the preference to receive email when they are added to or removed + # from a role, so remove them from the list before checking their + # other preferences. + if (grep(/Unconfirmed/, @$reasons) + && exists($prefs{"email${role}Unconfirmed"}) + && $prefs{"email${role}Unconfirmed"} eq '') + { + push(@excludedAddresses, $user); + next; + } + + # If the user was added to or removed from this role, and they + # prefer to receive email when that happens, send them mail. + # Note: This was originally written to send email when users + # were removed from roles and was later enhanced for additions, + # but for simplicity's sake the name "Removeme" was retained. + if (grep($_ eq $user, @{$force{$role}}) + && $prefs{"email${role}Removeme"} eq 'on') + { + push (@recipients, $user); + next; + } + + # If the user prefers to be included in mail about this change, + # or they haven't specified a preference for it (because they + # haven't visited the email preferences page since the preference + # was added, in which case we include them by default), add them + # to the list of recipients. + foreach my $reason (@$reasons) { + my $pref = "email$role$reason"; + if (!exists($prefs{$pref}) || $prefs{$pref} eq 'on') { + push(@recipients, $user); + next USER; + } + } + + # At this point there's no way the user wants to receive email + # about this change, so exclude them from the list of recipients. + push(@excludedAddresses, $user); + + } # for each user on the unfiltered list + + return @recipients; +} + +sub NewProcessOnePerson ($$$$$$$$$$$$$) { + my ($person, $count, $hlRef, $reasonsRef, $valueRef, $dmhRef, $fdRef, + $diffRef, $newcomments, $anyprivate, $start, + $id, $depbugsRef) = @_; + + my %values = %$valueRef; + my @headerlist = @$hlRef; + my @reasons = @$reasonsRef; + my %defmailhead = %$dmhRef; + my %fielddescription = %$fdRef; + my @diffparts = @$diffRef; + my @depbugs = @$depbugsRef; + + if ($seen{$person}) { + return; + } + + if ($nomail{$person}) { + return; + } + + + SendSQL("SELECT userid, (refreshed_when > " . SqlQuote($last_changed) . + ") FROM profiles WHERE login_name = " . SqlQuote($person)); + my ($userid, $current) = (FetchSQLData()); + + $seen{$person} = 1; + + detaint_natural($userid); + + if (!$current) { + DeriveGroup($userid); + } + + # if this person doesn't have permission to see info on this bug, + # return. + # + # XXX - This currently means that if a bug is suddenly given + # more restrictive permissions, people without those permissions won't + # see the action of restricting the bug itself; the bug will just + # quietly disappear from their radar. + # + return unless CanSeeBug($id, $userid); + + + # Drop any non-insiders if the comment is private + return if (Param("insidergroup") && + ($anyprivate != 0) && + (!UserInGroup(Param("insidergroup"), $userid))); + + # We shouldn't send changedmail if this is a dependency mail, and any of + # the depending bugs is not visible to the user. + foreach my $dep_id (@depbugs) { + my $save_id = $dep_id; + detaint_natural($dep_id) || warn("Unexpected Error: \@depbugs contains a non-numeric value: '$save_id'") + && return; + return unless CanSeeBug($dep_id, $userid); + } + + my %mailhead = %defmailhead; + + my $head = ""; + + foreach my $f (@headerlist) { + if ($mailhead{$f}) { + my $value = $values{$f}; + # If there isn't anything to show, don't include this header + if (! $value) { + next; + } + # Don't send estimated_time if user not in the group, or not enabled + if ($f ne 'estimated_time' || + UserInGroup(Param('timetrackinggroup'), $userid)) { + + my $desc = $fielddescription{$f}; + $head .= FormatDouble($desc, $value); + } + } + } + + # Build difftext (the actions) by verifying the user should see them + my $difftext = ""; + my $diffheader = ""; + my $add_diff; + foreach my $diff (@diffparts) { + + $add_diff = 0; + + if ($diff->{'fieldname'} eq 'estimated_time' || + $diff->{'fieldname'} eq 'remaining_time' || + $diff->{'fieldname'} eq 'work_time') { + if (UserInGroup(Param("timetrackinggroup"), $userid)) { + $add_diff = 1; + } + } else { + $add_diff = 1; + } + if ($add_diff) { + if ($diffheader ne $diff->{'header'}) { + $diffheader = $diff->{'header'}; + $difftext .= $diffheader; + } + $difftext .= $diff->{'text'}; + } + } + + if ($difftext eq "" && $newcomments eq "") { + # Whoops, no differences! + return; + } + + my $reasonsbody = "------- You are receiving this mail because: -------\n"; + + if (scalar(@reasons) == 0) { + $reasonsbody .= "Whoops! I have no idea!\n"; + } else { + foreach my $reason (@reasons) { + if ($reason eq 'AssignedTo') { + $reasonsbody .= "You are the assignee for the bug, or are watching the assignee.\n"; + } elsif ($reason eq 'Reporter') { + $reasonsbody .= "You reported the bug, or are watching the reporter.\n"; + } elsif ($reason eq 'QAcontact') { + $reasonsbody .= "You are the QA contact for the bug, or are watching the QA contact.\n"; + } elsif ($reason eq 'CC') { + $reasonsbody .= "You are on the CC list for the bug, or are watching someone who is.\n"; + } elsif ($reason eq 'Voter') { + $reasonsbody .= "You are a voter for the bug, or are watching someone who is.\n"; + } else { + $reasonsbody .= "Whoops! There is an unknown reason!\n"; + } + } + } + + my $isnew = ($start !~ m/[1-9]/); + + my %substs; + + # If an attachment was created, then add an URL. (Note: the 'g'lobal + # replace should work with comments with multiple attachments.) + + if ( $newcomments =~ /Created an attachment \(/ ) { + + my $showattachurlbase = + Param('urlbase') . "attachment.cgi?id="; + + $newcomments =~ s/(Created an attachment \(id=([0-9]+)\))/$1\n --> \(${showattachurlbase}$2&action=view\)/g; + } + + $person .= Param('emailsuffix'); +# 09/13/2000 cyeh@bluemartini.com +# If a bug is changed, don't put the word "Changed" in the subject mail +# since if the bug didn't change, you wouldn't be getting mail +# in the first place! see http://bugzilla.mozilla.org/show_bug.cgi?id=29820 +# for details. + $substs{"neworchanged"} = $isnew ? 'New: ' : ''; + $substs{"to"} = $person; + $substs{"cc"} = ''; + $substs{"bugid"} = $id; + if ($isnew) { + $substs{"diffs"} = $head . "\n\n" . $newcomments; + } else { + $substs{"diffs"} = $difftext . "\n\n" . $newcomments; + } + $substs{"summary"} = $values{'short_desc'}; + $substs{"reasonsheader"} = join(" ", @reasons); + $substs{"reasonsbody"} = $reasonsbody; + + my $template = Param("newchangedmail"); + + my $msg = PerformSubsts($template, \%substs); + + my $sendmailparam = "-ODeliveryMode=deferred"; + if (Param("sendmailnow")) { + $sendmailparam = ""; + } + + if ($enableSendMail == 1) { + open(SENDMAIL, "|/usr/lib/sendmail $sendmailparam -t -i") || + die "Can't open sendmail"; + + print SENDMAIL trim($msg) . "\n"; + close SENDMAIL; + } + push(@sentlist, $person); + return 1; +} + +1; diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 434785332..463247c86 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -259,6 +259,13 @@ sub create { # UserInGroup - you probably want to cache this 'UserInGroup' => \&::UserInGroup, + # SendBugMail - sends mail about a bug, using Bugzilla::BugMail.pm + 'SendBugMail' => sub { + my ($id, $mailrecipients) = (@_); + require Bugzilla::BugMail; + Bugzilla::BugMail::Send($id, $mailrecipients); + }, + # SyncAnyPendingShadowChanges # - called in the footer to sync the shadowdb 'SyncAnyPendingShadowChanges' => \&::SyncAnyPendingShadowChanges, diff --git a/CGI.pl b/CGI.pl index 7ded11ef2..eb74862fa 100644 --- a/CGI.pl +++ b/CGI.pl @@ -755,10 +755,7 @@ sub CheckIfVotedConfirmed { $vars->{'type'} = "votes"; $vars->{'id'} = $id; - $vars->{'mail'} = ""; - open(PMAIL, "-|") or exec('./processmail', $id); - $vars->{'mail'} .= $_ while ; - close(PMAIL); + $vars->{'mailrecipients'} = { 'changer' => $who }; $template->process("bug/process/results.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/attachment.cgi b/attachment.cgi index bb166e2f8..81037a723 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -574,23 +574,11 @@ sub insert } } - # Send mail to let people know the attachment has been created. Uses a - # special syntax of the "open" and "exec" commands to capture the output of - # "processmail", which "system" doesn't allow, without running the command - # through a shell, which backticks (``) do. - #system ("./processmail", $bugid , $::userid); - #my $mailresults = `./processmail $bugid $::userid`; - my $mailresults = ''; - open(PMAIL, "-|") or exec('./processmail', '-forcecc', $forcecc, - $::FORM{'bugid'}, $::COOKIE{'Bugzilla_login'}); - $mailresults .= $_ while ; - close(PMAIL); - # Define the variables and functions that will be passed to the UI template. + $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} }; $vars->{'bugid'} = $::FORM{'bugid'}; $vars->{'attachid'} = $attachid; $vars->{'description'} = $description; - $vars->{'mailresults'} = $mailresults; $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; $vars->{'contenttype'} = $::FORM{'contenttype'}; @@ -791,21 +779,10 @@ sub update } - # Send mail to let people know the bug has changed. Uses a special syntax - # of the "open" and "exec" commands to capture the output of "processmail", - # which "system" doesn't allow, without running the command through a shell, - # which backticks (``) do. - #system ("./processmail", $bugid , $::userid); - #my $mailresults = `./processmail $bugid $::userid`; - my $mailresults = ''; - open(PMAIL, "-|") or exec('./processmail', $bugid, DBID_to_name($::userid)); - $mailresults .= $_ while ; - close(PMAIL); - # Define the variables and functions that will be passed to the UI template. + $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} }; $vars->{'attachid'} = $::FORM{'id'}; $vars->{'bugid'} = $bugid; - $vars->{'mailresults'} = $mailresults; # Return the appropriate HTTP response headers. print "Content-Type: text/html\n\n"; diff --git a/post_bug.cgi b/post_bug.cgi index 18b761744..2a2bcb5fa 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -457,30 +457,16 @@ if (UserInGroup("editbugs")) { } } -# Assemble the -force* strings so this counts as "Added to this capacity" -my @ARGLIST = (); -if (@cc) { - push (@ARGLIST, "-forcecc", join(",", @cc)); -} - -push (@ARGLIST, "-forceowner", DBID_to_name($::FORM{assigned_to})); +# Email everyone the details of the new bug +$vars->{'mailrecipients'} = { 'cc' => \@cc, + 'owner' => DBID_to_name($::FORM{'assigned_to'}), + 'reporter' => $::COOKIE{'Bugzilla_login'}, + 'changer' => $::COOKIE{'Bugzilla_login'} }; if (defined $::FORM{'qa_contact'}) { - push (@ARGLIST, "-forceqacontact", DBID_to_name($::FORM{'qa_contact'})); + $vars->{'mailrecipients'}->{'qa'} = DBID_to_name($::FORM{'qa_contact'}); } -push (@ARGLIST, "-forcereporter", DBID_to_name($::userid)); - -push (@ARGLIST, $id, $::COOKIE{'Bugzilla_login'}); - -# Send mail to let people know the bug has been created. -# See attachment.cgi for explanation of why it's done this way. -my $mailresults = ''; -open(PMAIL, "-|") or exec('./processmail', @ARGLIST); -$mailresults .= $_ while ; -close(PMAIL); - -# Tell the user all about it $vars->{'id'} = $id; my $bug = new Bug($id, $::userid); $vars->{'bug'} = $bug; @@ -491,19 +477,10 @@ $vars->{'sentmail'} = []; push (@{$vars->{'sentmail'}}, { type => 'created', id => $id, - mail => $mailresults }); foreach my $i (@all_deps) { - my $mail = ""; - open(PMAIL, "-|") or exec('./processmail', $i, $::COOKIE{'Bugzilla_login'}); - $mail .= $_ while ; - close(PMAIL); - - push (@{$vars->{'sentmail'}}, { type => 'dep', - id => $i, - mail => $mail - }); + push (@{$vars->{'sentmail'}}, { type => 'dep', id => $i, }); } my @bug_list; diff --git a/process_bug.cgi b/process_bug.cgi index 6ca220377..cce3792ee 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -1360,8 +1360,8 @@ foreach my $id (@idlist) { LogActivityEntry($id, "bug_group", $groupDelNames, $groupAddNames, $whoid, $timestamp); $bug_changed = 1; - - my $removedCcString = ""; + + my @ccRemoved = (); if (defined $::FORM{newcc} || defined $::FORM{removecc} || defined $::FORM{masscc}) { # Get the current CC list for this bug my %oncc; @@ -1387,8 +1387,6 @@ foreach my $id (@idlist) { $oncc{$pid} = 0; } } - # Save off the removedCcString so it can be fed to processmail - $removedCcString = join (",", @removed); # If any changes were found, record it in the activity log if (scalar(@removed) || scalar(@added)) { @@ -1397,6 +1395,7 @@ foreach my $id (@idlist) { LogActivityEntry($id,"cc",$removed,$added,$whoid,$timestamp); $bug_changed = 1; } + @ccRemoved = @removed; } # We need to run processmail for dependson/blocked bugs if the dependencies @@ -1660,28 +1659,10 @@ foreach my $id (@idlist) { } SendSQL("UNLOCK TABLES"); - my @ARGLIST = (); - if ( $removedCcString ne "" ) { - push @ARGLIST, ("-forcecc", $removedCcString); - } - if ( $origOwner ne "" ) { - push @ARGLIST, ("-forceowner", $origOwner); - } - if ( $origQaContact ne "") { - push @ARGLIST, ( "-forceqacontact", $origQaContact); - } - push @ARGLIST, ($id, $::COOKIE{'Bugzilla_login'}); - - # Send mail to let people know the bug has been changed. Uses - # a special syntax of the "open" and "exec" commands to capture - # the output "processmail", which "system" doesn't allow - # (i.e. "system ('./processmail', $bugid , $::userid);"), without - # the insecurity of running the command through a shell via backticks - # (i.e. "my $mailresults = `./processmail $bugid $::userid`;"). - $vars->{'mail'} = ""; - open(PMAIL, "-|") or exec('./processmail', @ARGLIST); - $vars->{'mail'} .= $_ while ; - close(PMAIL); + $vars->{'mailrecipients'} = { 'cc' => \@ccRemoved, + 'owner' => $origOwner, + 'qa' => $origQaContact, + 'changer' => $::COOKIE{'Bugzilla_login'} }; $vars->{'id'} = $id; @@ -1710,11 +1691,9 @@ foreach my $id (@idlist) { CheckFormFieldDefined(\%::FORM,'comment'); SendSQL("INSERT INTO duplicates VALUES ($duplicate, $::FORM{'id'})"); - $vars->{'mail'} = ""; - open(PMAIL, "-|") or exec('./processmail', $duplicate, $::COOKIE{'Bugzilla_login'}); - $vars->{'mail'} .= $_ while ; - close(PMAIL); - + $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} + }; + $vars->{'id'} = $duplicate; $vars->{'type'} = "dupe"; @@ -1725,12 +1704,7 @@ foreach my $id (@idlist) { if ($check_dep_bugs) { foreach my $k (keys(%dependencychanged)) { - $vars->{'mail'} = ""; - open(PMAIL, "-|") - or exec('./processmail', $k, $::COOKIE{'Bugzilla_login'}); - $vars->{'mail'} .= $_ while ; - close(PMAIL); - + $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} }; $vars->{'id'} = $k; $vars->{'type'} = "dep"; diff --git a/processmail b/processmail deleted file mode 100755 index aa3628abd..000000000 --- a/processmail +++ /dev/null @@ -1,928 +0,0 @@ -#!/usr/bonsaitools/bin/perl -wT -# -*- 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 , -# Bryce Nesbitt -# Dan Mosedale -# Alan Raetz -# Jacob Steenhagen -# Matthew Tuck - -use strict; -use lib "."; - -require "globals.pl"; - -use RelationSet; - - -# Shut up misguided -w warnings about "used only once". -sub processmail_sillyness { - my $zz; - $zz = $::unconfirmedstate; -} - -$| = 1; - -umask(0); - -my $nametoexclude = ""; -my %nomail; - -my @excludedAddresses = (); - -# disable email flag for offline debugging work -my $enableSendMail = 1; - -my %force; -@{$force{'QAcontact'}} = (); -@{$force{'Owner'}} = (); -@{$force{'Reporter'}} = (); -@{$force{'CClist'}} = (); -@{$force{'Voter'}} = (); - - -my %seen; -my @sentlist; - -sub FormatTriple { - my ($a, $b, $c) = (@_); - $^A = ""; - my $temp = formline << 'END', $a, $b, $c; -^>>>>>>>>>>>>>>>>>>|^<<<<<<<<<<<<<<<<<<<<<<<<<<<|^<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ -END - ; # This semicolon appeases my emacs editor macros. :-) - return $^A; -} - -sub FormatDouble { - my ($a, $b) = (@_); - $a .= ":"; - $^A = ""; - my $temp = formline << 'END', $a, $b; -^>>>>>>>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ -END - ; # This semicolon appeases my emacs editor macros. :-) - return $^A; -} - - -sub ProcessOneBug { - my ($id) = (@_); - - my @headerlist; - my %values; - my %defmailhead; - my %fielddescription; - - my $msg = ""; - - SendSQL("SELECT name, description, mailhead FROM fielddefs " . - "ORDER BY sortkey"); - while (MoreSQLData()) { - my ($field, $description, $mailhead) = (FetchSQLData()); - push(@headerlist, $field); - $defmailhead{$field} = $mailhead; - $fielddescription{$field} = $description; - } - SendSQL("SELECT " . join(',', @::log_columns) . ", lastdiffed, now() " . - "FROM bugs WHERE bug_id = $id"); - my @row = FetchSQLData(); - foreach my $i (@::log_columns) { - $values{$i} = shift(@row); - } - $values{product} = get_product_name($values{product_id}); - $values{component} = get_component_name($values{component_id}); - - my ($start, $end) = (@row); - # $start and $end are considered safe because users can't touch them - trick_taint($start); - trick_taint($end); - - my $ccSet = new RelationSet(); - $ccSet->mergeFromDB("SELECT who FROM cc WHERE bug_id = $id"); - $values{'cc'} = $ccSet->toString(); - - my @voterList; - SendSQL("SELECT profiles.login_name FROM votes, profiles " . - "WHERE votes.bug_id = $id AND profiles.userid = votes.who"); - while (MoreSQLData()) { - push(@voterList, FetchOneColumn()); - } - - $values{'assigned_to'} = DBID_to_name($values{'assigned_to'}); - $values{'reporter'} = DBID_to_name($values{'reporter'}); - if ($values{'qa_contact'}) { - $values{'qa_contact'} = DBID_to_name($values{'qa_contact'}); - } - $values{'estimated_time'} = FormatTimeUnit($values{'estimated_time'}); - - my @dependslist; - SendSQL("SELECT dependson FROM dependencies WHERE - blocked = $id ORDER BY dependson"); - while (MoreSQLData()) { - push(@dependslist, FetchOneColumn()); - } - $values{'dependson'} = join(",", @dependslist); - - my @blockedlist; - SendSQL("SELECT blocked FROM dependencies WHERE - dependson = $id ORDER BY blocked"); - while (MoreSQLData()) { - push(@blockedlist, FetchOneColumn()); - } - $values{'blocked'} = join(",", @blockedlist); - - my @diffs; - - - SendSQL("SELECT profiles.login_name, fielddefs.description, " . - " bug_when, removed, added, attach_id, fielddefs.name " . - "FROM bugs_activity, fielddefs, profiles " . - "WHERE bug_id = $id " . - " AND fielddefs.fieldid = bugs_activity.fieldid " . - " AND profiles.userid = who " . - " AND bug_when > '$start' " . - " AND bug_when <= '$end' " . - "ORDER BY bug_when" - ); - - while (MoreSQLData()) { - my @row = FetchSQLData(); - push(@diffs, \@row); - } - - my $difftext = ""; - my $diffheader = ""; - my $diffpart = {}; - my @diffparts; - my $lastwho = ""; - foreach my $ref (@diffs) { - my ($who, $what, $when, $old, $new, $attachid, $fieldname) = (@$ref); - $diffpart = {}; - if ($who ne $lastwho) { - $lastwho = $who; - $diffheader = "\n$who" . Param('emailsuffix') . " changed:\n\n"; - $diffheader .= FormatTriple("What ", "Removed", "Added"); - $diffheader .= ('-' x 76) . "\n"; - } - $what =~ s/^(Attachment )?/Attachment #$attachid / if $attachid; - if( $fieldname eq 'estimated_time' || - $fieldname eq 'remaining_time' ) { - $old = FormatTimeUnit($old); - $new = FormatTimeUnit($new); - } - $difftext = FormatTriple($what, $old, $new); - $diffpart->{'header'} = $diffheader; - $diffpart->{'fieldname'} = $fieldname; - $diffpart->{'text'} = $difftext; - push(@diffparts, $diffpart); - } - - - my $deptext = ""; - - my $resid = - - SendSQL("SELECT bugs_activity.bug_id, bugs.short_desc, fielddefs.name, " . - " removed, added " . - "FROM bugs_activity, bugs, dependencies, fielddefs ". - "WHERE bugs_activity.bug_id = dependencies.dependson " . - " AND bugs.bug_id = bugs_activity.bug_id ". - " AND dependencies.blocked = $id " . - " AND fielddefs.fieldid = bugs_activity.fieldid" . - " AND (fielddefs.name = 'bug_status' " . - " OR fielddefs.name = 'resolution') " . - " AND bug_when > '$start' " . - " AND bug_when <= '$end' " . - "ORDER BY bug_when, bug_id"); - - my $thisdiff = ""; - my $lastbug = ""; - my $interestingchange = 0; - my $depbug = 0; - my @depbugs; - while (MoreSQLData()) { - my ($summary, $what, $old, $new); - ($depbug, $summary, $what, $old, $new) = (FetchSQLData()); - if ($depbug ne $lastbug) { - if ($interestingchange) { - $deptext .= $thisdiff; - } - $lastbug = $depbug; - my $urlbase = Param("urlbase"); - $thisdiff = - "\nBug $id depends on bug $depbug, which changed state.\n\n" . - "Bug $depbug Summary: $summary\n" . - "${urlbase}show_bug.cgi?id=$depbug\n\n"; - $thisdiff .= FormatTriple("What ", "Old Value", "New Value"); - $thisdiff .= ('-' x 76) . "\n"; - $interestingchange = 0; - } - $thisdiff .= FormatTriple($fielddescription{$what}, $old, $new); - if ($what eq 'bug_status' && IsOpenedState($old) ne IsOpenedState($new)) { - $interestingchange = 1; - } - - push(@depbugs, $depbug); - } - - if ($interestingchange) { - $deptext .= $thisdiff; - } - - $deptext = trim($deptext); - - if ($deptext) { - #$difftext = trim($difftext . "\n\n" . $deptext); - $diffpart->{'text'} = trim("\n\n" . $deptext); - push(@diffparts, $diffpart); - } - - - my ($newcomments, $anyprivate) = GetLongDescriptionAsText($id, $start, $end); - - # - # Start of email filtering code - # - my $count = 0; - - # Get a list of the reasons a user might receive email about the bug. - my @currentEmailAttributes = - getEmailAttributes(\%values, \@diffs, $newcomments); - - my (@assigned_toList,@reporterList,@qa_contactList,@ccList) = (); - - #open(LOG, ">>/tmp/maillog"); - #print LOG "\nBug ID: $id CurrentEmailAttributes:"; - #print LOG join(',', @currentEmailAttributes) . "\n"; - - @excludedAddresses = (); # zero out global list - - @assigned_toList = filterEmailGroup('Owner', - \@currentEmailAttributes, - $values{'assigned_to'}); - @reporterList = filterEmailGroup('Reporter', - \@currentEmailAttributes, - $values{'reporter'}); - if (Param('useqacontact') && $values{'qa_contact'}) { - @qa_contactList = filterEmailGroup('QAcontact', - \@currentEmailAttributes, - $values{'qa_contact'}); - } else { - @qa_contactList = (); - } - - @ccList = filterEmailGroup('CClist', \@currentEmailAttributes, - $values{'cc'}); - - @voterList = filterEmailGroup('Voter', \@currentEmailAttributes, - join(',',@voterList)); - - my @emailList = (@assigned_toList, @reporterList, - @qa_contactList, @ccList, @voterList); - - # only need one entry per person - my @allEmail = (); - my %AlreadySeen = (); - my $checkperson = ""; - foreach my $person (@emailList) { - # don't modify the original so it sends out with the right case - # based on who came first. - $checkperson = lc($person); - if ( !($AlreadySeen{$checkperson}) ) { - push(@allEmail,$person); - $AlreadySeen{$checkperson}++; - } - } - - #print LOG "\nbug $id email sent: " . join(',', @allEmail) . "\n"; - - @excludedAddresses = filterExcludeList(\@excludedAddresses, - \@allEmail); - - # print LOG "excluded: " . join(',',@excludedAddresses) . "\n\n"; - - foreach my $person ( @allEmail ) { - my @reasons; - - $count++; - - push(@reasons, 'AssignedTo') if lsearch(\@assigned_toList, $person) != -1; - push(@reasons, 'Reporter') if lsearch(\@reporterList, $person) != -1; - push(@reasons, 'QAcontact') if lsearch(\@qa_contactList, $person) != -1; - push(@reasons, 'CC') if lsearch(\@ccList, $person) != -1; - push(@reasons, 'Voter') if lsearch(\@voterList, $person) != -1; - - if ( !defined(NewProcessOnePerson($person, $count, \@headerlist, - \@reasons, \%values, - \%defmailhead, - \%fielddescription, \@diffparts, - $newcomments, - $anyprivate, $start, $id, - \@depbugs))) - { - - # if a value is not returned, this means that the person - # was not sent mail. add them to the excludedAddresses list. - # it will be filtered later for dups. - # - push @excludedAddresses, $person; - - } - } - - - SendSQL("UPDATE bugs SET lastdiffed = '$end', delta_ts = delta_ts " . - "WHERE bug_id = $id"); - - # Filter the exclude list for dupes one last time - @excludedAddresses = filterExcludeList(\@excludedAddresses, - \@sentlist); - if (@sentlist) { - print "Email sent to: " . join(", ", @sentlist) ."
\n"; - } else { - print "Email sent to: no one
\n"; - } - - if (@excludedAddresses) { - print "Excluding: " . join(", ", @excludedAddresses) . "\n"; - } - - print "
If you wish to tweak the kinds of mail Bugzilla sends you, you can"; - print " change your preferences
\n"; - -} - -# When one person is in different fields on one bug, they may be -# excluded from email because of one relationship to the bug (eg -# they're the QA contact) but included because of another (eg they -# also reported the bug). Inclusion takes precedence, so this -# function looks for and removes any users from the exclude list who -# are also on the include list. Additionally, it removes duplicate -# entries from the exclude list. -# -# Arguments are the exclude list and the include list; the cleaned up -# exclude list is returned. -# -sub filterExcludeList ($$) { - - if ($#_ != 1) { - die ("filterExcludeList called with wrong number of args"); - } - - my ($refExcluded, $refAll) = @_; - - my @excludedAddrs = @$refExcluded; - my @allEmail = @$refAll; - my @tmpList = @excludedAddrs; - my (@result,@uniqueResult) = (); - my %alreadySeen; - - foreach my $excluded (@tmpList) { - - push (@result,$excluded); - foreach my $included (@allEmail) { - - # match found, so we remove the entry - if (lc($included) eq lc($excluded)) { - pop(@result); - last; - } - } - } - - # only need one entry per person - my $checkperson = ""; - - foreach my $person (@result) { - $checkperson = lc($person); - if ( !($alreadySeen{$checkperson}) ) { - push(@uniqueResult,$person); - $alreadySeen{$checkperson}++; - } - } - - return @uniqueResult; -} - -# if the Status was changed to Resolved or Verified -# set the Resolved flag -# -# else if Severity, Status, Target Milestone OR Priority fields have any change -# set the Status flag -# -# else if Keywords has changed -# set the Keywords flag -# -# else if CC has changed -# set the CC flag -# -# if the Comments field shows an attachment -# set the Attachment flag -# -# else if Comments exist -# set the Comments flag -# -# if no flags are set and there was some other field change -# set the Status flag -# -sub getEmailAttributes (\%\@$) { - - my ($bug, $fieldDiffs, $commentField) = @_; - my (@flags,@uniqueFlags,%alreadySeen) = (); - - # Add a flag if the status of the bug is "unconfirmed". - if ($bug->{'bug_status'} eq $::unconfirmedstate) { - push (@flags, 'Unconfirmed') - }; - - foreach my $ref (@$fieldDiffs) { - my ($who, $fieldName, $when, $old, $new) = (@$ref); - - #print qq{field: $fieldName $new
}; - - # the STATUS will be flagged for Severity, Status, Target Milestone and - # Priority changes - # - if ( $fieldName eq 'Status' && ($new eq 'RESOLVED' || $new eq 'VERIFIED')) { - push (@flags, 'Resolved'); - } - elsif ( $fieldName eq 'Severity' || $fieldName eq 'Status' || - $fieldName eq 'Priority' || $fieldName eq 'Target Milestone') { - push (@flags, 'Status'); - } elsif ( $fieldName eq 'Keywords') { - push (@flags, 'Keywords'); - } elsif ( $fieldName eq 'CC') { - push (@flags, 'CC'); - } - - # These next few lines are for finding out who's been added - # to the Owner, QA, CC, etc. fields. It does not effect - # the @flags array at all, but is run here because it does - # effect filtering later and we're already in the loop. - if ($fieldName eq 'AssignedTo') { - push (@{$force{'Owner'}}, $new); - } elsif ($fieldName eq 'QAContact') { - push (@{$force{'QAcontact'}}, $new); - } elsif ($fieldName eq 'CC') { - my @added = split (/[ ,]/, $new); - push (@{$force{'CClist'}}, @added); - } - } - - if ( $commentField =~ /Created an attachment \(/ ) { - push (@flags, 'Attachments'); - } - elsif ( ($commentField ne '') && !(scalar(@flags) == 1 && $flags[0] eq 'Resolved')) { - push (@flags, 'Comments'); - } - - # default fallthrough for any unflagged change is 'Other' - if ( @flags == 0 && @$fieldDiffs != 0 ) { - push (@flags, 'Other'); - } - - # only need one flag per attribute type - foreach my $flag (@flags) { - if ( !($alreadySeen{$flag}) ) { - push(@uniqueFlags,$flag); - $alreadySeen{$flag}++; - } - } - #print "\nEmail Attributes: ", join(' ,',@uniqueFlags), "
\n"; - - # catch-all default, just in case the above logic is faulty - if ( @uniqueFlags == 0) { - push (@uniqueFlags, 'Comments'); - } - - return @uniqueFlags; -} - -sub filterEmailGroup ($$$) { - # This function figures out who should receive email about the bug - # based on the user's role with regard to the bug (assignee, reporter - # etc.), the changes that occurred (new comments, attachment added, - # status changed etc.) and the user's email preferences. - - # Returns a filtered list of those users who would receive email - # about these changes, and adds the names of those users who would - # not receive email about them to the global @excludedAddresses list. - - my ($role, $reasons, $users) = @_; - - # Make a list of users to filter. - my @users = split( /,/ , $users ); - - # Treat users who are in the process of being removed from this role - # as if they were still in it. - push @users, @{$force{$role}}; - - # If this installation supports user watching, add to the list those - # users who are watching other users already on the list. By doing - # this here, we allow watchers to inherit the roles of the people - # they are watching while at the same time filtering email for watchers - # based on their own preferences. - if (Param("supportwatchers") && scalar(@users)) { - # Convert the unfiltered list of users into an SQL-quoted, - # comma-separated string suitable for use in an SQL query. - my $watched = join(",", map(SqlQuote($_), @users)); - SendSQL("SELECT watchers.login_name - FROM watch, profiles AS watchers, profiles AS watched - WHERE watched.login_name IN ($watched) - AND watched.userid = watch.watched - AND watch.watcher = watchers.userid"); - push (@users, FetchOneColumn()) while MoreSQLData(); - } - - # Initialize the list of recipients. - my @recipients = (); - - USER: foreach my $user (@users) { - next unless $user; - - # Get the user's unique ID, and if the user is not registered - # (no idea why unregistered users should even be on this list, - # but the code that was here before I re-wrote it allows this), - # then we do not have any preferences for them, so assume the - # default preference to receive all mail for any reason. - my $userid = DBname_to_id($user); - if (!$userid) { - push(@recipients, $user); - next; - } - - # Get the user's email preferences from the database. - SendSQL("SELECT emailflags FROM profiles WHERE userid = $userid"); - my $prefs = FetchOneColumn(); - - # If the user's preferences are empty, assume the default preference - # to receive all mail. This happens when the installation upgraded - # from a version of Bugzilla without email preferences to one with - # them, but the user has not set their preferences yet. - if (!defined($prefs) || $prefs !~ /email/) { - push(@recipients, $user); - next; - } - - # Write the user's preferences into a Perl record indexed by - # preference name. We pass the value "255" to the split function - # because otherwise split will trim trailing null fields, causing - # Perl to generate lots of warnings. Any suitably large number - # would do. - my %prefs = split(/~/, $prefs, 255); - - # If this user is the one who made the change in the first place, - # and they prefer not to receive mail about their own changes, - # filter them from the list. - if (lc($user) eq $nametoexclude && $prefs{'ExcludeSelf'} eq 'on') { - push(@excludedAddresses, $user); - next; - } - - # If the user doesn't want to receive email about unconfirmed - # bugs, that setting overrides their other preferences, including - # the preference to receive email when they are added to or removed - # from a role, so remove them from the list before checking their - # other preferences. - if (grep(/Unconfirmed/, @$reasons) - && exists($prefs{"email${role}Unconfirmed"}) - && $prefs{"email${role}Unconfirmed"} eq '') - { - push(@excludedAddresses, $user); - next; - } - - # If the user was added to or removed from this role, and they - # prefer to receive email when that happens, send them mail. - # Note: This was originally written to send email when users - # were removed from roles and was later enhanced for additions, - # but for simplicity's sake the name "Removeme" was retained. - if (grep($_ eq $user, @{$force{$role}}) - && $prefs{"email${role}Removeme"} eq 'on') - { - push (@recipients, $user); - next; - } - - # If the user prefers to be included in mail about this change, - # or they haven't specified a preference for it (because they - # haven't visited the email preferences page since the preference - # was added, in which case we include them by default), add them - # to the list of recipients. - foreach my $reason (@$reasons) { - my $pref = "email$role$reason"; - if (!exists($prefs{$pref}) || $prefs{$pref} eq 'on') { - push(@recipients, $user); - next USER; - } - } - - # At this point there's no way the user wants to receive email - # about this change, so exclude them from the list of recipients. - push(@excludedAddresses, $user); - - } # for each user on the unfiltered list - - return @recipients; -} - -sub NewProcessOnePerson ($$$$$$$$$$$$$) { - my ($person, $count, $hlRef, $reasonsRef, $valueRef, $dmhRef, $fdRef, - $diffRef, $newcomments, $anyprivate, $start, - $id, $depbugsRef) = @_; - - my %values = %$valueRef; - my @headerlist = @$hlRef; - my @reasons = @$reasonsRef; - my %defmailhead = %$dmhRef; - my %fielddescription = %$fdRef; - my @diffparts = @$diffRef; - my @depbugs = @$depbugsRef; - - if ($seen{$person}) { - return; - } - - if ($nomail{$person}) { - return; - } - - - SendSQL("SELECT userid, (refreshed_when > " . SqlQuote($::last_changed) . ") " . - "FROM profiles WHERE login_name = " . SqlQuote($person)); - my ($userid, $current) = (FetchSQLData()); - - $seen{$person} = 1; - - detaint_natural($userid); - - if (!$current) { - DeriveGroup($userid); - } - - # if this person doesn't have permission to see info on this bug, - # return. - # - # XXX - This currently means that if a bug is suddenly given - # more restrictive permissions, people without those permissions won't - # see the action of restricting the bug itself; the bug will just - # quietly disappear from their radar. - # - return unless CanSeeBug($id, $userid); - - - # Drop any non-insiders if the comment is private - return if (Param("insidergroup") && - ($anyprivate != 0) && - (!UserInGroup(Param("insidergroup"), $userid))); - - # We shouldn't send changedmail if this is a dependency mail, and any of - # the depending bugs is not visible to the user. - foreach my $dep_id (@depbugs) { - my $save_id = $dep_id; - detaint_natural($dep_id) || warn("Unexpected Error: \@depbugs contains a non-numeric value: '$save_id'") - && return; - return unless CanSeeBug($dep_id, $userid); - } - - my %mailhead = %defmailhead; - - my $head = ""; - - foreach my $f (@headerlist) { - if ($mailhead{$f}) { - my $value = $values{$f}; - # If there isn't anything to show, don't include this header - if (! $value) { - next; - } - # Don't send estimated_time if user not in the group, or not enabled - if ($f ne 'estimated_time' || - UserInGroup(Param('timetrackinggroup'), $userid)) { - - my $desc = $fielddescription{$f}; - $head .= FormatDouble($desc, $value); - } - } - } - - # Build difftext (the actions) by verifying the user should see them - my $difftext = ""; - my $diffheader = ""; - my $add_diff; - foreach my $diff (@diffparts) { - - $add_diff = 0; - - if ($diff->{'fieldname'} eq 'estimated_time' || - $diff->{'fieldname'} eq 'remaining_time' || - $diff->{'fieldname'} eq 'work_time') { - if (UserInGroup(Param("timetrackinggroup"), $userid)) { - $add_diff = 1; - } - } else { - $add_diff = 1; - } - if ($add_diff) { - if ($diffheader ne $diff->{'header'}) { - $diffheader = $diff->{'header'}; - $difftext .= $diffheader; - } - $difftext .= $diff->{'text'}; - } - } - - if ($difftext eq "" && $newcomments eq "") { - # Whoops, no differences! - return; - } - - my $reasonsbody = "------- You are receiving this mail because: -------\n"; - - if (scalar(@reasons) == 0) { - $reasonsbody .= "Whoops! I have no idea!\n"; - } else { - foreach my $reason (@reasons) { - if ($reason eq 'AssignedTo') { - $reasonsbody .= "You are the assignee for the bug, or are watching the assignee.\n"; - } elsif ($reason eq 'Reporter') { - $reasonsbody .= "You reported the bug, or are watching the reporter.\n"; - } elsif ($reason eq 'QAcontact') { - $reasonsbody .= "You are the QA contact for the bug, or are watching the QA contact.\n"; - } elsif ($reason eq 'CC') { - $reasonsbody .= "You are on the CC list for the bug, or are watching someone who is.\n"; - } elsif ($reason eq 'Voter') { - $reasonsbody .= "You are a voter for the bug, or are watching someone who is.\n"; - } else { - $reasonsbody .= "Whoops! There is an unknown reason!\n"; - } - } - } - - my $isnew = ($start !~ m/[1-9]/); - - my %substs; - - # If an attachment was created, then add an URL. (Note: the 'g'lobal - # replace should work with comments with multiple attachments.) - - if ( $newcomments =~ /Created an attachment \(/ ) { - - my $showattachurlbase = - Param('urlbase') . "attachment.cgi?id="; - - $newcomments =~ s/(Created an attachment \(id=([0-9]+)\))/$1\n --> \(${showattachurlbase}$2&action=view\)/g; - } - - $person .= Param('emailsuffix'); -# 09/13/2000 cyeh@bluemartini.com -# If a bug is changed, don't put the word "Changed" in the subject mail -# since if the bug didn't change, you wouldn't be getting mail -# in the first place! see http://bugzilla.mozilla.org/show_bug.cgi?id=29820 -# for details. - $substs{"neworchanged"} = $isnew ? 'New: ' : ''; - $substs{"to"} = $person; - $substs{"cc"} = ''; - $substs{"bugid"} = $id; - if ($isnew) { - $substs{"diffs"} = $head . "\n\n" . $newcomments; - } else { - $substs{"diffs"} = $difftext . "\n\n" . $newcomments; - } - $substs{"summary"} = $values{'short_desc'}; - $substs{"reasonsheader"} = join(" ", @reasons); - $substs{"reasonsbody"} = $reasonsbody; - - my $template = Param("newchangedmail"); - - my $msg = PerformSubsts($template, \%substs); - - my $sendmailparam = "-ODeliveryMode=deferred"; - if (Param("sendmailnow")) { - $sendmailparam = ""; - } - - if ($enableSendMail == 1) { - open(SENDMAIL, "|/usr/lib/sendmail $sendmailparam -t -i") || - die "Can't open sendmail"; - - print SENDMAIL trim($msg) . "\n"; - close SENDMAIL; - } - push(@sentlist, $person); - return 1; -} - -# Code starts here - -ConnectToDatabase(); -GetVersionTable(); - -if (open(FID, ") { - $nomail{trim($_)} = 1; - } - close FID; -} - -# Since any email recipients must be rederived if the user has not -# been rederived since the most recent group change, figure out when that -# is once and determine the need to rederive users using the same DB access -# that gets the user's email address each time a person is processed. -# -SendSQL("SELECT MAX(last_changed) FROM groups"); -($::last_changed) = FetchSQLData(); - -if ($#ARGV >= 0 && $ARGV[0] eq "regenerate") { - print "Regenerating is no longer required or supported\n"; - exit; -} - -if ($#ARGV >= 0 && $ARGV[0] eq "-forcecc") { - shift(@ARGV); - foreach my $i (split(/,/, shift(@ARGV))) { - push(@{$force{'CClist'}}, trim($i)); - } -} - -if ($#ARGV >= 0 && $ARGV[0] eq "-forceowner") { - shift(@ARGV); - @{$force{'Owner'}} = (trim(shift(@ARGV))); -} - -if ($#ARGV >= 0 && $ARGV[0] eq "-forceqacontact") { - shift(@ARGV); - @{$force{'QAcontact'}} = (trim(shift(@ARGV))); -} - -if ($#ARGV >= 0 && $ARGV[0] eq "-forcereporter") { - shift(@ARGV); - @{$force{'Reporter'}} = trim(shift(@ARGV)); -} - -if (($#ARGV < 0) || ($#ARGV > 1)) { - print "Usage:\n processmail {bugid} {nametoexclude} " . - "[-forcecc list,of,users]\n [-forceowner name] " . - "[-forceqacontact name]\n [-forcereporter name]\nor\n" . - " processmail rescanall\n"; - exit; -} - -if ($#ARGV == 1) { - $nametoexclude = lc($ARGV[1]); -} - -if ($ARGV[0] eq "rescanall") { - print "Collecting bug ids...\n"; - SendSQL("select bug_id, lastdiffed, delta_ts from bugs where lastdiffed < delta_ts AND delta_ts < date_sub(now(), INTERVAL 30 minute) order by bug_id"); - my @list; - while (my @row = FetchSQLData()) { - my $time = $row[2]; - if ($time =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) { - $time = "$1-$2-$3 $4:$5:$6"; - } - print STDERR "Bug $row[0] has unsent mail. lastdiffed is $row[1], delta_ts is $time.\n"; - push @list, $row[0]; - } - if (scalar(@list) > 0) { - print STDERR scalar(@list) . " bugs found with possibly unsent mail\n"; - print STDERR "Updating bugs, sending mail if required\n"; - } else { - print "All appropriate mail appears to have been sent\n" - } - foreach my $id (@list) { - if (detaint_natural($id)) { - ProcessOneBug($id); - } - } -} else { - my $bugnum; - if ($ARGV[0] =~ m/^([1-9][0-9]*)$/) { - $bugnum = $1; - } else { - print "Error calling processmail (bug id is not an integer)
\n"; - exit; - } - ProcessOneBug($bugnum); -} - -exit; diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 49c007f7f..f5b8be210 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -150,6 +150,31 @@ if (exists $::FORM{'cleangroupsnow'}) { "- reduced from $before records to $after records"); } +if (exists $::FORM{'rescanallBugMail'}) { + require Bugzilla::BugMail; + + Status("OK, now attempting to send unsent mail"); + SendSQL("SELECT bug_id FROM bugs WHERE lastdiffed < delta_ts AND + delta_ts < date_sub(now(), INTERVAL 30 minute) ORDER BY bug_id"); + my @list; + while (MoreSQLData()) { + push (@list, FetchOneColumn()); + } + + Status(scalar(@list) . ' bugs found with possibly unsent mail.'); + + foreach my $bugid (@list) { + Bugzilla::BugMail::Send($bugid); + } + + if (scalar(@list) > 0) { + Status("Unsent mail has been sent."); + } + + PutFooter(); + exit; +} + print "OK, now running sanity checks.

\n"; ########################################################################### @@ -683,7 +708,7 @@ while (@row = FetchSQLData()) { if (@badbugs > 0) { Alert("Bugs that have changes but no mail sent for at least half an hour: " . join (", ", @badbugs)); - print("Run processmail rescanall to fix this

\n"); + print qq{Send these mails.

\n}; } ########################################################################### diff --git a/template/en/default/attachment/created.html.tmpl b/template/en/default/attachment/created.html.tmpl index 56e71ccba..2b985bd0c 100644 --- a/template/en/default/attachment/created.html.tmpl +++ b/template/en/default/attachment/created.html.tmpl @@ -26,7 +26,6 @@ # contenttype: string. The Content Type we attached it as. # contenttypemethod: string. How we got the content type of the attachment. # Possible values: autodetect, list, manual. - # mailresults: string. who was mailed, and who wasn't. #%] [% PROCESS global/header.html.tmpl @@ -42,7 +41,7 @@ to Bug #[% bugid %] Created - [% mailresults %] + [% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = bugid %] [% IF contenttypemethod == 'autodetect' %]

diff --git a/template/en/default/attachment/updated.html.tmpl b/template/en/default/attachment/updated.html.tmpl index 2c7ceb24d..df5f4665a 100644 --- a/template/en/default/attachment/updated.html.tmpl +++ b/template/en/default/attachment/updated.html.tmpl @@ -23,7 +23,6 @@ [%# INTERFACE: # bugid: integer. ID of the bug we are updating. # attachid: integer. ID of the attachment we just attached. - # mailresults: string. Who was mailed and who wasn't. #%] [% PROCESS global/header.html.tmpl @@ -40,7 +39,7 @@ attachment [% attachid %] of bug [% bugid %] submitted - [% mailresults %] + [% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = bugid %]

diff --git a/template/en/default/bug/process/bugmail.html.tmpl b/template/en/default/bug/process/bugmail.html.tmpl new file mode 100644 index 000000000..5445ef9cd --- /dev/null +++ b/template/en/default/bug/process/bugmail.html.tmpl @@ -0,0 +1,70 @@ + +[%# 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): Bradley Baetz + # J. Paul Reed + #%] + +[%# INTERFACE: + # mailing_bugid: string. ID of the bug this mail is concerning. + # mailrecipients: hash. People involved in this change. Hash has up to five + # elements: + # changer: string. The login name of the user who made the + # change. + # + # For bug changes where people need to be notified: + # owner: string. The login name of the bug assignee. + # reporter: string. The login name of the bug reporter. + # qacontact: string. The login name of the bug's QA contact. + # Optional. + # cc: list of strings. The login names of those on the CC + # list. + #%] + +[% mail = SendBugMail(mailing_bugid, mailrecipients) %] + +[% PROCESS emails + description = "Email sent to" + names = mail.sent +%] +
+[% PROCESS emails + description = "Excluding" + names = mail.excluded +%] +
+
+ If you wish to tweak the kinds of mail Bugzilla sends you, you can + change your preferences. +
+ + +[%############################################################################%] +[%# Block for a set of email addresses #%] +[%############################################################################%] + +[% BLOCK emails %] + [% description %]: + [% IF names.size > 0 %] + [%+ FOREACH name = names %] + [% name %][% ", " UNLESS loop.last() %] + [% END %] + [% ELSE %] + no one + [% END %] +[% END %] diff --git a/template/en/default/bug/process/results.html.tmpl b/template/en/default/bug/process/results.html.tmpl index 99dfbc11d..cc3e0df95 100644 --- a/template/en/default/bug/process/results.html.tmpl +++ b/template/en/default/bug/process/results.html.tmpl @@ -25,6 +25,8 @@ # type: string; the type of change/check that was made: "bug" when a bug # is changed, "dupe" when a duplication notation is added to a bug, # and "dep" when a bug is checked for changes to its dependencies. + # + # mailrecipients: hash; BugMail recipient params. Optional. #%] [% DEFAULT type="bug" %] @@ -43,7 +45,7 @@

[% title.$type %]

- [% mail %] + [% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = id %]
Back To BUG# [% id %] -- cgit v1.2.3-65-gdbad From 9665c0e6a0ee98cdd113e11b86d3efab474464c0 Mon Sep 17 00:00:00 2001 From: "jake%bugzilla.org" <> Date: Thu, 27 Mar 2003 08:06:37 +0000 Subject: Bug 196433 - Bugzilla now uses /usr/bin/perl as the shebang line r=justdave a=justdave --- UPGRADING-pre-2.8 | 4 ++-- attachment.cgi | 2 +- buglist.cgi | 2 +- checksetup.pl | 2 +- colchange.cgi | 2 +- collectstats.pl | 2 +- contrib/mysqld-watcher.pl | 2 +- createaccount.cgi | 2 +- describecomponents.cgi | 2 +- describekeywords.cgi | 2 +- docs/makedocs.pl | 2 +- docs/sgml/faq.sgml | 42 ++++++++++++++++++++++++++++++------------ docs/sgml/installation.sgml | 37 +------------------------------------ docs/xml/faq.xml | 42 ++++++++++++++++++++++++++++++------------ docs/xml/installation.xml | 37 +------------------------------------ doeditparams.cgi | 2 +- duplicates.cgi | 2 +- editcomponents.cgi | 2 +- editflagtypes.cgi | 2 +- editgroups.cgi | 2 +- editkeywords.cgi | 2 +- editmilestones.cgi | 2 +- editparams.cgi | 2 +- editproducts.cgi | 2 +- editusers.cgi | 2 +- editversions.cgi | 2 +- enter_bug.cgi | 2 +- importxml.pl | 2 +- index.cgi | 2 +- long_list.cgi | 2 +- move.pl | 2 +- page.cgi | 2 +- post_bug.cgi | 2 +- process_bug.cgi | 2 +- query.cgi | 2 +- queryhelp.cgi | 2 +- quips.cgi | 2 +- relogin.cgi | 2 +- report.cgi | 2 +- reports.cgi | 2 +- request.cgi | 2 +- runtests.pl | 2 +- runtests.sh | 9 +-------- sanitycheck.cgi | 2 +- show_activity.cgi | 2 +- show_bug.cgi | 2 +- showattachment.cgi | 2 +- showdependencygraph.cgi | 2 +- showdependencytree.cgi | 2 +- sidebar.cgi | 2 +- t/002goodperl.t | 4 ++-- token.cgi | 2 +- userprefs.cgi | 2 +- votes.cgi | 2 +- whineatnews.pl | 2 +- xml.cgi | 2 +- 56 files changed, 116 insertions(+), 157 deletions(-) (limited to 'attachment.cgi') diff --git a/UPGRADING-pre-2.8 b/UPGRADING-pre-2.8 index b01d98269..9d0da5e24 100644 --- a/UPGRADING-pre-2.8 +++ b/UPGRADING-pre-2.8 @@ -73,7 +73,7 @@ was. Nothing uses this yet, but it still should be recorded. You should also run this script to populate the new field: -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w use diagnostics; use strict; require "globals.pl"; @@ -149,7 +149,7 @@ that submitted the text. This bug has been fixed, so that no further changes like that will happen. But to fix problems that have already crept into your database, you can run the following perl script (which is slow and ugly, but does work:) -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w use diagnostics; use strict; require "globals.pl"; diff --git a/attachment.cgi b/attachment.cgi index 81037a723..01201a192 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/buglist.cgi b/buglist.cgi index 33a32f824..ff12dba35 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/checksetup.pl b/checksetup.pl index a8ea7b1e1..7a7548c01 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/colchange.cgi b/colchange.cgi index 22ff8c562..5e28a4622 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/collectstats.pl b/collectstats.pl index bdc027a68..5d72827b2 100755 --- a/collectstats.pl +++ b/collectstats.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/contrib/mysqld-watcher.pl b/contrib/mysqld-watcher.pl index 30945a5ff..a70e2250a 100755 --- a/contrib/mysqld-watcher.pl +++ b/contrib/mysqld-watcher.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # # The contents of this file are subject to the Mozilla Public # License Version 1.1 (the "License"); you may not use this file diff --git a/createaccount.cgi b/createaccount.cgi index 0550f42b9..dec8e716c 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/describecomponents.cgi b/describecomponents.cgi index a1a6f0049..ec46b3d9d 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/describekeywords.cgi b/describekeywords.cgi index 07f08d09b..0ff538b63 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/docs/makedocs.pl b/docs/makedocs.pl index 7a245a470..d9d1aa7b6 100644 --- a/docs/makedocs.pl +++ b/docs/makedocs.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/docs/sgml/faq.sgml b/docs/sgml/faq.sgml index df80794cd..d0f78b75a 100644 --- a/docs/sgml/faq.sgml +++ b/docs/sgml/faq.sgml @@ -204,25 +204,43 @@ - Why do the scripts say "/usr/bonsaitools/bin/perl" instead of - "/usr/bin/perl" or something else? + What is /usr/bonsaitools/bin/perl? - Mozilla.org uses /usr/bonsaitools/bin/perl, because originally - Terry wanted a place to put a version of Perl and other tools - that was strictly under his control. - - - We always recommend that, if possible, you keep the path - as /usr/bonsaitools/bin/perl, and simply add symlink. - This will make upgrading - your Bugzilla much easier in the future. - + Bugzilla used to have the path to perl on the shebang line set to + /usr/bonsaitools/bin/perl because when + Terry first started writing the code for mozilla.org he needed a + version of Perl and other tools that were completely under his + control. This location was abandoned for the 2.18 release in favor + of the more sensible /usr/bin/perl. If you + installed an older verion of Bugzilla and created the symlink we + suggested, you can remove it now (provided that you don't have + anything else, such as Bonsai, using it and you don't intend to + reinstall an older version of Bugzilla). + + + + + My perl is not located at /usr/bin/perl, is + there an easy way to change it everywhere it needs to be changed? + + + + + Yes, the following bit of perl magic will change all the shebang + lines. Be sure to change /usr/local/bin/perl + to your path to the perl binary. + + +perl -pi -e 's@#\!/usr/bin/perl@#\!/usr/local/bin/perl@' *cgi *pl + + + diff --git a/docs/sgml/installation.sgml b/docs/sgml/installation.sgml index d1fa2401b..9f94feb7a 100644 --- a/docs/sgml/installation.sgml +++ b/docs/sgml/installation.sgml @@ -522,41 +522,6 @@ until you run the post-install checksetup.pl script, which locks down your installation. - - Lastly, you'll need to set up a symbolic link to - /usr/bonsaitools/bin/perl - for the correct location of your Perl executable (probably - /usr/bin/perl). - Otherwise you must hack all the .cgi files to change where they look - for Perl. This can be done using the following Perl one-liner, but - I suggest using the symlink approach to avoid upgrade hassles. - - - - Bonsaitools is the name Terry Weissman, the - original author of Bugzilla, created - for his suite of webtools at the time he created Bugzilla and several - other tools in use at mozilla.org. He created a directory, - /usr/bonsaitools to house his specific versions - of perl and other utilities. This usage is still current at - bugzilla.mozilla.org, - but in general most other places do not use it. You can either edit - the paths at the start of each perl file to the correct location of - perl on your system, or simply bow to history and create a - /usr/bonsaitools and /usr/bonsaitools/bin - directory, placing a symlink to perl on your system - inside /usr/bonsaitools/bin - - - - - -perl -pi -e 's@#\!/usr/bonsaitools/bin/perl@#\!/usr/bin/perl@' *cgi *pl Bug.pm syncshadowdb - - - Change /usr/bin/perl to match the location - of Perl on your machine. -
@@ -1185,7 +1150,7 @@ system("C:\\perl\\bin\\perl", "$webdotbase","-Tpng","-o","$pngfilename","$filena url="http://httpd.apache.org/docs-2.0/mod/core.html#scriptinterpretersource">ScriptInterpreterSource directive in your Apache config, if you don't do this, you'll have to modify the first line of every script to contain your path to - perl instead of /usr/bonsaitools/bin/perl. + perl instead of /usr/bin/perl. diff --git a/docs/xml/faq.xml b/docs/xml/faq.xml index df80794cd..d0f78b75a 100644 --- a/docs/xml/faq.xml +++ b/docs/xml/faq.xml @@ -204,25 +204,43 @@ - Why do the scripts say "/usr/bonsaitools/bin/perl" instead of - "/usr/bin/perl" or something else? + What is /usr/bonsaitools/bin/perl? - Mozilla.org uses /usr/bonsaitools/bin/perl, because originally - Terry wanted a place to put a version of Perl and other tools - that was strictly under his control. - - - We always recommend that, if possible, you keep the path - as /usr/bonsaitools/bin/perl, and simply add symlink. - This will make upgrading - your Bugzilla much easier in the future. - + Bugzilla used to have the path to perl on the shebang line set to + /usr/bonsaitools/bin/perl because when + Terry first started writing the code for mozilla.org he needed a + version of Perl and other tools that were completely under his + control. This location was abandoned for the 2.18 release in favor + of the more sensible /usr/bin/perl. If you + installed an older verion of Bugzilla and created the symlink we + suggested, you can remove it now (provided that you don't have + anything else, such as Bonsai, using it and you don't intend to + reinstall an older version of Bugzilla). + + + + + My perl is not located at /usr/bin/perl, is + there an easy way to change it everywhere it needs to be changed? + + + + + Yes, the following bit of perl magic will change all the shebang + lines. Be sure to change /usr/local/bin/perl + to your path to the perl binary. + + +perl -pi -e 's@#\!/usr/bin/perl@#\!/usr/local/bin/perl@' *cgi *pl + + + diff --git a/docs/xml/installation.xml b/docs/xml/installation.xml index d1fa2401b..9f94feb7a 100644 --- a/docs/xml/installation.xml +++ b/docs/xml/installation.xml @@ -522,41 +522,6 @@ until you run the post-install checksetup.pl script, which locks down your installation. - - Lastly, you'll need to set up a symbolic link to - /usr/bonsaitools/bin/perl - for the correct location of your Perl executable (probably - /usr/bin/perl). - Otherwise you must hack all the .cgi files to change where they look - for Perl. This can be done using the following Perl one-liner, but - I suggest using the symlink approach to avoid upgrade hassles. - - - - Bonsaitools is the name Terry Weissman, the - original author of Bugzilla, created - for his suite of webtools at the time he created Bugzilla and several - other tools in use at mozilla.org. He created a directory, - /usr/bonsaitools to house his specific versions - of perl and other utilities. This usage is still current at - bugzilla.mozilla.org, - but in general most other places do not use it. You can either edit - the paths at the start of each perl file to the correct location of - perl on your system, or simply bow to history and create a - /usr/bonsaitools and /usr/bonsaitools/bin - directory, placing a symlink to perl on your system - inside /usr/bonsaitools/bin - - - - - -perl -pi -e 's@#\!/usr/bonsaitools/bin/perl@#\!/usr/bin/perl@' *cgi *pl Bug.pm syncshadowdb - - - Change /usr/bin/perl to match the location - of Perl on your machine. -
@@ -1185,7 +1150,7 @@ system("C:\\perl\\bin\\perl", "$webdotbase","-Tpng","-o","$pngfilename","$filena url="http://httpd.apache.org/docs-2.0/mod/core.html#scriptinterpretersource">ScriptInterpreterSource directive in your Apache config, if you don't do this, you'll have to modify the first line of every script to contain your path to - perl instead of /usr/bonsaitools/bin/perl. + perl instead of /usr/bin/perl. diff --git a/doeditparams.cgi b/doeditparams.cgi index d37e8b207..48c39bc7a 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/duplicates.cgi b/duplicates.cgi index 175fb649a..a9a3031f3 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editcomponents.cgi b/editcomponents.cgi index 4ee147453..35c6426b2 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 6a2301251..d7794ff93 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editgroups.cgi b/editgroups.cgi index 1b793c6d3..ca653b77a 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editkeywords.cgi b/editkeywords.cgi index ed298ef9b..4d11a4aae 100755 --- a/editkeywords.cgi +++ b/editkeywords.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editmilestones.cgi b/editmilestones.cgi index e9d5d77e5..4bcf8b19b 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # diff --git a/editparams.cgi b/editparams.cgi index 372eb4d7b..89099823f 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editproducts.cgi b/editproducts.cgi index a34812c8d..9592c02ad 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editusers.cgi b/editusers.cgi index fee00a4e0..4e423c2d5 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/editversions.cgi b/editversions.cgi index 5f420af43..f3e828d98 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/enter_bug.cgi b/enter_bug.cgi index 894de886f..9784b81a3 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/importxml.pl b/importxml.pl index 50139ba40..7979dfcf9 100755 --- a/importxml.pl +++ b/importxml.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/index.cgi b/index.cgi index f6e11ac21..1bdaa5351 100755 --- a/index.cgi +++ b/index.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/long_list.cgi b/long_list.cgi index 5bb19956c..4c787a34e 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/move.pl b/move.pl index ab0794243..fa34e758d 100755 --- a/move.pl +++ b/move.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/page.cgi b/page.cgi index 960774cc4..48fafb380 100755 --- a/page.cgi +++ b/page.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/post_bug.cgi b/post_bug.cgi index c319e8024..670920d43 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/process_bug.cgi b/process_bug.cgi index 94c74fcf5..9476b7d68 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/query.cgi b/query.cgi index f6f8c87a5..470e3dfbd 100755 --- a/query.cgi +++ b/query.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/queryhelp.cgi b/queryhelp.cgi index eb9893bd8..175605d9a 100755 --- a/queryhelp.cgi +++ b/queryhelp.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/quips.cgi b/quips.cgi index 067fa950e..02109fcda 100755 --- a/quips.cgi +++ b/quips.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/relogin.cgi b/relogin.cgi index 493f5c200..c0182de49 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/report.cgi b/report.cgi index 9978e2002..2a081606b 100755 --- a/report.cgi +++ b/report.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/reports.cgi b/reports.cgi index 5c802ccfe..d3b1d9431 100755 --- a/reports.cgi +++ b/reports.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/request.cgi b/request.cgi index 29c19f27f..790916359 100755 --- a/request.cgi +++ b/request.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/runtests.pl b/runtests.pl index 376a4a1e4..d5059fdc0 100755 --- a/runtests.pl +++ b/runtests.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/runtests.sh b/runtests.sh index 164ec3acf..d6f3d1a54 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,10 +1,3 @@ #!/bin/sh -TEST_VERBOSE=0 -for f in $*; do - if [ "$f" = "--verbose" ] ; then - TEST_VERBOSE="--verbose" - fi -done - -/usr/bonsaitools/bin/perl runtests.pl ${TEST_VERBOSE} +/usr/bin/perl runtests.pl $* diff --git a/sanitycheck.cgi b/sanitycheck.cgi index a28858f43..16a123967 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/show_activity.cgi b/show_activity.cgi index 14b4149ba..c748c3df7 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/show_bug.cgi b/show_bug.cgi index 80e53e616..c4c05f42c 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/showattachment.cgi b/showattachment.cgi index 2471332fc..bfe9ef988 100755 --- a/showattachment.cgi +++ b/showattachment.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index 77a1d4dc0..fd0a75cb4 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/showdependencytree.cgi b/showdependencytree.cgi index 4ee9e4cc8..9149296b7 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/sidebar.cgi b/sidebar.cgi index 7cf823aaa..7a054abc2 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/t/002goodperl.t b/t/002goodperl.t index f6f14adb5..1cfb6a06f 100644 --- a/t/002goodperl.t +++ b/t/002goodperl.t @@ -48,7 +48,7 @@ foreach my $file (@testitems) { $file =~ m/.*\.(.*)/; my $ext = $1; - if ($file_line1 !~ /\/usr\/bonsaitools\/bin\/perl/) { + if ($file_line1 !~ m#/usr/bin/perl#) { ok(1,"$file does not have a shebang"); } else { my $flags; @@ -71,7 +71,7 @@ foreach my $file (@testitems) { next; } - if ($file_line1 =~ m#/usr/bonsaitools/bin/perl -$flags#) { + if ($file_line1 =~ m#/usr/bin/perl -$flags#) { ok(1,"$file uses -$flags"); } else { ok(0,"$file is MISSING -$flags --WARNING"); diff --git a/token.cgi b/token.cgi index 9a7efb5c5..5da824bae 100755 --- a/token.cgi +++ b/token.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/userprefs.cgi b/userprefs.cgi index 271dbd847..156c6a2dd 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/votes.cgi b/votes.cgi index b8fbfa847..6b35fc027 100755 --- a/votes.cgi +++ b/votes.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/whineatnews.pl b/whineatnews.pl index a0a30205e..330cf7cbd 100755 --- a/whineatnews.pl +++ b/whineatnews.pl @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -w +#!/usr/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public diff --git a/xml.cgi b/xml.cgi index f93c0e5c0..103838f86 100755 --- a/xml.cgi +++ b/xml.cgi @@ -1,4 +1,4 @@ -#!/usr/bonsaitools/bin/perl -wT +#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public -- cgit v1.2.3-65-gdbad From b259c4b4252cde8e7926690f6ba419d2ee7c59fc Mon Sep 17 00:00:00 2001 From: "bbaetz%acm.org" <> Date: Wed, 2 Apr 2003 20:35:00 +0000 Subject: Bug 199813 - Make all users of ThrowUserError pass $vars in explicitly. r=gerv a=justdave --- Bugzilla/Error.pm | 93 +++++++++++++++++++++++++ Bugzilla/Search.pm | 19 +++-- CGI.pl | 22 +----- attachment.cgi | 28 +++++--- describecomponents.cgi | 4 +- duplicates.cgi | 23 +++--- globals.pl | 5 +- post_bug.cgi | 18 ++--- process_bug.cgi | 50 +++++++------ template/en/default/global/user-error.html.tmpl | 42 ++++------- token.cgi | 4 +- userprefs.cgi | 3 +- 12 files changed, 193 insertions(+), 118 deletions(-) create mode 100644 Bugzilla/Error.pm (limited to 'attachment.cgi') diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm new file mode 100644 index 000000000..64314121a --- /dev/null +++ b/Bugzilla/Error.pm @@ -0,0 +1,93 @@ +# -*- 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): Bradley Baetz + +use strict; + +package Bugzilla::Error; + +use base qw(Exporter); + +@Bugzilla::Error::EXPORT = qw(ThrowUserError); + +sub ThrowUserError { + my ($error, $vars, $unlock_tables) = @_; + + $vars ||= {}; + + $vars->{error} = $error; + + # Need to do this until User.pm goes in, so that the footer is correct + $vars->{user} = $::vars->{user}; + + Bugzilla->dbh->do("UNLOCK TABLES") if $unlock_tables; + + # XXX - mod_perl + print "Content-type: text/html\n\n" if !$::vars->{'header_done'}; + + my $template = Bugzilla->template; + $template->process("global/user-error.html.tmpl", $vars) + || &::ThrowTemplateError($template->error()); + + exit; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Error - Error handling utilities for Bugzilla + +=head1 SYNOPSIS + + use Bugzilla::Error; + + ThrowUserError("error_tag", + { foo => 'bar' }); + +=head1 DESCRIPTION + +Various places throughout the Bugzilla codebase need to report errors to the +user. The C family of functions allow this to be done in a +generic and localisable manner. + +=head1 FUNCTIONS + +=over 4 + +=item C + +This function takes an error tag as the first argument, and an optional hashref +of variables as a second argument. These are used by the +I template to format the error, using the passed +in variables as required. + +An optional third argument may be supplied. If present (and defined), then the +error handling code will unlock the database tables. In the long term, this +argument will go away, to be replaced by transactional C calls. There +is no timeframe for doing so, however. + +=back + +=head1 SEE ALSO + +L diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index ead9156ee..1637671f0 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -103,8 +103,8 @@ sub init { my $c = trim($params->param('votes')); if ($c ne "") { if ($c !~ /^[0-9]*$/) { - $::vars->{'value'} = $c; - &::ThrowUserError("illegal_at_least_x_votes"); + &::ThrowUserError("illegal_at_least_x_votes", + { value => $c }); } push(@specialchart, ["votes", "greaterthan", $c - 1]); } @@ -207,8 +207,8 @@ sub init { if (@clist) { push(@specialchart, \@clist); } else { - $::vars->{'email'} = $email; - &::ThrowUserError("missing_email_type"); + ThrowUserError("missing_email_type", + { email => $email }); } } @@ -217,8 +217,8 @@ sub init { my $c = trim($params->param('changedin')); if ($c ne "") { if ($c !~ /^[0-9]*$/) { - $::vars->{'value'} = $c; - &::ThrowUserError("illegal_changed_in_last_x_days"); + &::ThrowUserError("illegal_changed_in_last_x_days", + { value => $c }); } push(@specialchart, ["changedin", "lessthan", $c + 1]); @@ -558,8 +558,8 @@ sub init { push(@list, "$table.keywordid = $id"); } else { - $::vars->{'keyword'} = $v; - &::ThrowUserError("unknown_keyword"); + ThrowUserError("unknown_keyword", + { keyword => $v }); } } my $haveawordterm; @@ -992,8 +992,7 @@ sub SqlifyDate { } my $date = str2time($str); if (!defined($date)) { - $::vars->{'date'} = $str; - &::ThrowUserError("illegal_date"); + &::ThrowUserError("illegal_date", { date => $str }); } return time2str("%Y-%m-%d %H:%M:%S", $date); } diff --git a/CGI.pl b/CGI.pl index 8ce589ced..74b81c9da 100644 --- a/CGI.pl +++ b/CGI.pl @@ -35,6 +35,7 @@ use lib "."; use Bugzilla::Util; use Bugzilla::Config; use Bugzilla::Constants; +use Bugzilla::Error; # Shut up misguided -w warnings about "used only once". For some reason, # "use vars" chokes on me when I try it here. @@ -252,8 +253,7 @@ sub CheckEmailSyntax { my ($addr) = (@_); my $match = Param('emailregexp'); if ($addr !~ /$match/ || $addr =~ /[\\\(\)<>&,;:"\[\] \t\r\n]/) { - $vars->{'addr'} = $addr; - ThrowUserError("illegal_email_address"); + ThrowUserError("illegal_email_address", { addr => $addr }); } } @@ -327,24 +327,6 @@ sub ThrowCodeError { exit; } -# For errors made by the user. -sub ThrowUserError { - ($vars->{'error'}, my $extra_vars, my $unlock_tables) = (@_); - - SendSQL("UNLOCK TABLES") if $unlock_tables; - - # Copy the extra_vars into the vars hash - foreach my $var (keys %$extra_vars) { - $vars->{$var} = $extra_vars->{$var}; - } - - print "Content-type: text/html\n\n" if !$vars->{'header_done'}; - $template->process("global/user-error.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - - exit; -} - # This function should only be called if a template->process() fails. # It tries another template first, because often one template being # broken or missing doesn't mean that they all are. But it falls back on diff --git a/attachment.cgi b/attachment.cgi index 01201a192..51a154645 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -171,7 +171,8 @@ sub validateCanEdit "attach_id = $attach_id AND submitter_id = $::userid"); FetchSQLData() - || ThrowUserError("illegal_attachment_edit"); + || ThrowUserError("illegal_attachment_edit", + { attach_id => $attach_id }); } sub validateCanChangeAttachment @@ -183,7 +184,8 @@ sub validateCanChangeAttachment AND bugs.bug_id = attachments.bug_id"); my $productid = FetchOneColumn(); CanEditProductId($productid) - || ThrowUserError("illegal_attachment_edit"); + || ThrowUserError("illegal_attachment_edit", + { attach_id => $attachid }); } sub validateCanChangeBug @@ -194,7 +196,8 @@ sub validateCanChangeBug WHERE bug_id = $bugid"); my $productid = FetchOneColumn(); CanEditProductId($productid) - || ThrowUserError("illegal_attachment_edit"); + || ThrowUserError("illegal_attachment_edit_bug", + { bug_id => $bugid }); } sub validateDescription @@ -249,8 +252,8 @@ sub validateContentType if ( $::FORM{'contenttype'} !~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ ) { - $vars->{'contenttype'} = $::FORM{'contenttype'}; - ThrowUserError("invalid_content_type"); + ThrowUserError("invalid_content_type", + { contenttype => $::FORM{'contenttype'} }); } } @@ -292,11 +295,11 @@ sub validateData # Make sure the attachment does not exceed the maximum permitted size my $len = length($data); if ($maxsize && $len > $maxsize) { - $vars->{'filesize'} = sprintf("%.0f", $len/1024); + my $vars = { filesize => sprintf("%.0f", $len/1024) }; if ( $::FORM{'ispatch'} ) { - ThrowUserError("patch_too_large"); + ThrowUserError("patch_too_large", $vars); } else { - ThrowUserError("file_too_large"); + ThrowUserError("file_too_large", $vars); } } @@ -328,6 +331,9 @@ sub validateObsolete # Make sure the attachment id is valid and the user has permissions to view # the bug to which it is attached. foreach my $attachid (@{$::MFORM{'obsolete'}}) { + # my $vars after ThrowCodeError is updated to not use the global + # vars hash + $vars->{'attach_id'} = $attachid; detaint_natural($attachid) @@ -338,7 +344,7 @@ sub validateObsolete # Make sure the attachment exists in the database. MoreSQLData() - || ThrowUserError("invalid_attach_id"); + || ThrowUserError("invalid_attach_id", $vars); my ($bugid, $isobsolete, $description) = FetchSQLData(); @@ -663,8 +669,8 @@ sub update my $bugid = FetchSQLData(); unless ($bugid) { - $vars->{'bug_id'} = $bugid; - ThrowUserError("invalid_bug_id"); + ThrowUserError("invalid_bug_id", + { bug_id => $bugid }); } # Lock database tables in preparation for updating the attachment. diff --git a/describecomponents.cgi b/describecomponents.cgi index ec46b3d9d..bdb824b82 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -83,8 +83,8 @@ my $product = $::FORM{'product'}; my $product_id = get_product_id($product); if (!$product_id) { - $::vars->{'product'} = $product; - ThrowUserError("invalid_product_name"); + ThrowUserError("invalid_product_name", + { product => $product }); } # Make sure the user is authorized to access this product. diff --git a/duplicates.cgi b/duplicates.cgi index a9a3031f3..1a3c08a9f 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -83,8 +83,8 @@ my $product_id; if ($product) { $product_id = get_product_id($product); if (!$product_id) { - $vars->{'product'} = $product; - ThrowUserError("invalid_product_name"); + ThrowUserError("invalid_product_name", + { product => $product }); } } @@ -109,17 +109,17 @@ if (!tie(%dbmcount, 'AnyDBM_File', "data/duplicates/dupes$today", if ($!{ENOENT}) { if (!tie(%dbmcount, 'AnyDBM_File', "data/duplicates/dupes$yesterday", O_RDONLY, 0644)) { - $vars->{'today'} = $today; + my $vars = { today => $today }; if ($!{ENOENT}) { - ThrowUserError("no_dupe_stats"); + ThrowUserError("no_dupe_stats", $vars); } else { $vars->{'error_msg'} = $!; - ThrowUserError("no_dupe_stats_error_yesterday"); + ThrowUserError("no_dupe_stats_error_yesterday", $vars); } } } else { - $vars->{'error_msg'} = $!; - ThrowUserError("no_dupe_stats_error_today"); + ThrowUserError("no_dupe_stats_error_today", + { error_msg => $! }); } } @@ -146,10 +146,11 @@ if (!tie(%before, 'AnyDBM_File', "data/duplicates/dupes$whenever", O_RDONLY, 0644)) { # Ignore file not found errors if (!$!{ENOENT}) { - $vars->{'error_msg'} = $!; - $vars->{'changedsince'} = $changedsince; - $vars->{'whenever'} = $whenever; - ThrowUserError("no_dupe_stats_error_whenever"); + ThrowUserError("no_dupe_stats_error_whenever", + { error_msg => $!, + changedsince => $changedsince, + whenever => $whenever, + }); } } else { # Calculate the deltas diff --git a/globals.pl b/globals.pl index ae656d9ec..38833ce15 100644 --- a/globals.pl +++ b/globals.pl @@ -127,7 +127,6 @@ sub AppendComment { # zero or more digits, OR we have a period followed by one or more digits if ($work_time !~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/) { ThrowUserError("need_numeric_value"); - return; } } else { $work_time = 0 }; @@ -863,8 +862,8 @@ sub DBNameToIdAndCheck { return $result; } - $::vars->{'name'} = $name; - ThrowUserError("invalid_username"); + ThrowUserError("invalid_username", + { name => $name }); } diff --git a/post_bug.cgi b/post_bug.cgi index 670920d43..37a43afb4 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -78,8 +78,8 @@ ValidateComment($comment); my $product = $::FORM{'product'}; my $product_id = get_product_id($product); if (!$product_id) { - $vars->{'product'} = $product; - ThrowUserError("invalid_product_name"); + ThrowUserError("invalid_product_name", + { product => $product }); } # Set cookies @@ -230,8 +230,8 @@ if ($::FORM{'keywords'} && UserInGroup("editbugs")) { } my $i = GetKeywordIdFromName($keyword); if (!$i) { - $vars->{'keyword'} = $keyword; - ThrowUserError("unknown_keyword"); + ThrowUserError("unknown_keyword", + { keyword => $keyword }); } if (!$keywordseen{$i}) { push(@keywordlist, $i); @@ -301,8 +301,10 @@ if (UserInGroup("editbugs") && defined($::FORM{'dependson'})) { foreach my $i (@isect) { $both = $both . GetBugLink($i, "#" . $i) . " "; } - $vars->{'both'} = $both; - ThrowUserError("dependency_loop_multi", undef, "abort"); + + ThrowUserError("dependency_loop_multi", + { both => $both }, + "abort"); } } my $tmp = $me; @@ -337,8 +339,8 @@ if (UserInGroup(Param("timetrackinggroup")) && if ($est_time =~ /^(?:\d+(?:\.\d*)?|\.\d+)$/) { $sql .= SqlQuote($est_time) . "," . SqlQuote($est_time); } else { - $vars->{'field'} = "estimated_time"; - ThrowUserError("need_positive_number"); + ThrowUserError("need_positive_number", + { field => 'estimated_time' }); } } else { $sql .= "0, 0"; diff --git a/process_bug.cgi b/process_bug.cgi index 9476b7d68..53e8f77d6 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -223,7 +223,12 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct) $vars->{'oldvalue'} = $::oldproduct; $vars->{'newvalue'} = $::FORM{'product'}; $vars->{'field'} = 'product'; - ThrowUserError("illegal_change", undef, "abort"); + ThrowUserError("illegal_change", + { oldvalue => $::oldproduct, + newvalue => $::FORM{'product'}, + field => 'product', + }, + "abort"); } CheckFormField(\%::FORM, 'product', \@::legal_product); @@ -689,7 +694,7 @@ if (Param("usebugaliases") && defined($::FORM{'alias'})) { # Make sure the alias is unique. my $escaped_alias = SqlQuote($alias); - $vars->{'alias'} = $alias; + my $vars = { alias => $alias }; SendSQL("SELECT bug_id FROM bugs WHERE alias = $escaped_alias " . "AND bug_id != $idlist[0]"); @@ -697,17 +702,17 @@ if (Param("usebugaliases") && defined($::FORM{'alias'})) { if ($id) { $vars->{'bug_link'} = GetBugLink($id, "Bug $id"); - ThrowUserError("alias_in_use"); + ThrowUserError("alias_in_use", $vars); } # Make sure the alias isn't just a number. if ($alias =~ /^\d+$/) { - ThrowUserError("alias_is_numeric"); + ThrowUserError("alias_is_numeric", $vars); } # Make sure the alias has no commas or spaces. if ($alias =~ /[, ]/) { - ThrowUserError("alias_has_comma_or_space"); + ThrowUserError("alias_has_comma_or_space", $vars); } } @@ -750,8 +755,8 @@ if (UserInGroup(Param('timetrackinggroup'))) { DoComma(); $::query .= "$field = " . SqlQuote($er_time); } else { - $vars->{'field'} = $field; - ThrowUserError("need_positive_number"); + ThrowUserError("need_positive_number", + field => $field); } } } @@ -946,8 +951,8 @@ SWITCH: for ($::FORM{'knob'}) { SendSQL("SELECT bug_id FROM bugs where bug_id = " . SqlQuote($checkid)); $checkid = FetchOneColumn(); if (!$checkid) { - $vars->{'bug_id'} = $checkid; - ThrowUserError("invalid_bug_id"); + ThrowUserError("invalid_bug_id", + { bug_id => $checkid }); } $::FORM{'comment'} .= "\n\n*** This bug has been marked as a duplicate of $num ***"; $duplicate = $num; @@ -975,8 +980,8 @@ if ($::FORM{'keywords'}) { } my $i = GetKeywordIdFromName($keyword); if (!$i) { - $vars->{keyword} = $keyword; - ThrowUserError("unknown_keyword"); + ThrowUserError("unknown_keyword", + { keyword => $keyword }); } if (!$keywordseen{$i}) { push(@keywordlist, $i); @@ -1098,6 +1103,7 @@ foreach my $id (@idlist) { if (exists $::FORM{$col}) { if (!CheckCanChangeField($col, $id, $oldvalues[$i], $::FORM{$col})) { # More fun hacking... don't display component_id + my $vars; if ($col eq 'component_id') { $vars->{'oldvalue'} = get_component_name($oldhash{'component_id'}); $vars->{'newvalue'} = $::FORM{'component'}; @@ -1108,23 +1114,23 @@ foreach my $id (@idlist) { $vars->{'newvalue'} = $::FORM{$col}; $vars->{'field'} = $col; } - ThrowUserError("illegal_change", undef, "abort"); + ThrowUserError("illegal_change", $vars, "abort"); } } $i++; } $oldhash{'product'} = get_product_name($oldhash{'product_id'}); if (!CanEditProductId($oldhash{'product_id'})) { - $vars->{'product'} = $oldhash{'product'}; - ThrowUserError("product_edit_denied"); + ThrowUserError("product_edit_denied", + { product => $oldhash{'product'} }); } if (defined $::FORM{'product'} && $::FORM{'product'} ne $::FORM{'dontchange'} && $::FORM{'product'} ne $oldhash{'product'} && !CanEnterProduct($::FORM{'product'})) { - $vars->{'product'} = $::FORM{'product'}; - ThrowUserError("entry_access_denied"); + ThrowUserError("entry_access_denied", + { product => $::FORM{'product'} }); } if ($requiremilestone) { my $value = $::FORM{'target_milestone'}; @@ -1135,8 +1141,9 @@ foreach my $id (@idlist) { SqlQuote($oldhash{'product'})); if ($value eq FetchOneColumn()) { SendSQL("UNLOCK TABLES"); - $vars->{'bug_id'} = $id; - ThrowUserError("milestone_required", undef, "abort"); + ThrowUserError("milestone_required", + { bug_id => $id }, + "abort"); } } if (defined $::FORM{'delta_ts'} && $::FORM{'delta_ts'} ne $delta_ts) { @@ -1213,9 +1220,10 @@ foreach my $id (@idlist) { foreach my $i (@isect) { $both = $both . GetBugLink($i, "#" . $i) . " "; } - - $vars->{'both'} = $both; - ThrowUserError("dependency_loop_multi", undef, "abort"); + + ThrowUserError("dependency_loop_multi", + { both => $both }, + "abort"); } } my $tmp = $me; diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index a89d029eb..fe1d9e223 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -34,14 +34,7 @@ [% DEFAULT title = "Error" %] [% error_message = BLOCK %] - [% IF error == "aaa_example_error_tag" %] - [% title = "Example Error" %] - This is an example error. The title is set above. This text is the body - of the error. It can contain arbitrary HTML, and also references - to any [% parameters %] which you may have set before calling - ThrowUserError. - - [% ELSIF error == "account_creation_disabled" %] + [% IF error == "account_creation_disabled" %] [% title = "Account Creation Disabled" %] Account self-creation has been disabled or restricted.
@@ -159,7 +152,7 @@ [% title = "Nice Try..." %] Nice try, [% user.login FILTER html %], but it doesn't really make sense to mark a bug as a duplicate of itself, - does it? + does it? [% ELSIF error == "email_change_in_progress" %] [% title = "Email Change Already In Progress" %] @@ -207,7 +200,7 @@ [% ELSIF error == "flag_type_sortkey_invalid" %] [% title = "Flag Type Sort Key Invalid" %] The sort key must be an integer between 0 and 32767 inclusive. - It cannot be [% variables.sortkey %]. + It cannot be [% sortkey FILTER html %]. [% ELSIF error == "illegal_at_least_x_votes" %] [% title = "Your Query Makes No Sense" %] @@ -217,6 +210,10 @@ [% ELSIF error == "illegal_attachment_edit" %] [% title = "Unauthorised Action" %] You are not authorised to edit attachment [% attach_id %]. + + [% ELSIF error == "illegal_attachment_edit_bug" %] + [% title = "Unauthorised Action" %] + You are not authorised to edit attachments on bug [% bug_id %]. [% ELSIF error == "illegal_attachment_is_patch" %] [% title = "Your Query Makes No Sense" %] @@ -507,18 +504,6 @@ [% title = "Access Denied" %] You do not have the permissions necessary to view reports for this product. - [% ELSIF error == "requestee_too_short" %] - [% title = "Requestee Name Too Short" %] - One or two characters match too many users, so please enter at least - three characters of the name/email address of the user you want to set - the flag. - - [% ELSIF error == "requestee_too_many_matches" %] - [% title = "Requestee String Matched Too Many Times" %] - The string [% requestee FILTER html %] matched more than - 100 users. Enter more of the name to bring the number of matches - down to a reasonable amount. - [% ELSIF error == "require_component" %] [% title = "Component Needed" %] You must choose a component to file this bug in. If necessary, @@ -569,7 +554,7 @@ [% ELSIF error == "unknown_tab" %] [% title = "Unknown Tab" %] - [% current_tab_name FILTER html %] is not a legal tab name. + [% current_tab_name FILTER html %] is not a legal tab name. [% ELSIF error == "votes_must_be_nonnegative" %] [% title = "Votes Must Be Non-negative" %] @@ -593,14 +578,13 @@ [% ELSIF error == "zero_length_file" %] [% title = "File Is Empty" %] - The file you are trying to attach is empty! + The file you are trying to attach is empty! [% ELSE %] - [%# Cope with legacy calling convention, where "error" was the string - # to print. - #%] - - [% error %] + [% title = "Error string not found" %] + The user error string [% error FILTER html %] was not found. + Please send email to [% Param("maintainer") %] describing the steps taken + to obtain this message. [% END %] [% END %] diff --git a/token.cgi b/token.cgi index 5da824bae..afe6d0361 100755 --- a/token.cgi +++ b/token.cgi @@ -231,9 +231,9 @@ sub changeEmail { # The new email address should be available as this was # confirmed initially so cancel token if it is not still available if (! ValidateNewUser($new_email,$old_email)) { - $vars->{'email'} = $new_email; + $vars->{'email'} = $new_email; # Needed for Token::Cancel's mail Token::Cancel($::token,"account_exists"); - ThrowUserError("account_exists"); + ThrowUserError("account_exists", { email => $new_email } ); } # Update the user's login name in the profiles table and delete the token diff --git a/userprefs.cgi b/userprefs.cgi index 156c6a2dd..fa340f50f 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -385,7 +385,8 @@ SWITCH: for ($current_tab_name) { DoPermissions(); last SWITCH; }; - ThrowUserError("current_tab_name"); + ThrowUserError("unknown_tab", + { current_tab_name => $current_tab_name }); } # Generate and return the UI (HTML page) from the appropriate template. -- cgit v1.2.3-65-gdbad From 47c010537c77f8e7e09e6c19246cdbecbb7b5a26 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Fri, 25 Apr 2003 12:41:20 +0000 Subject: Fix for bug 179510: takes group restrictions into account when sending request notifications r=bbaetz,jpreed a=justdave --- Attachment.pm | 8 +- Bugzilla/Attachment.pm | 8 +- Bugzilla/Flag.pm | 95 +++++++++++++++++++-- Bugzilla/FlagType.pm | 59 ++++++++++++- Bugzilla/Util.pm | 6 +- attachment.cgi | 33 +++++++- globals.pl | 106 +++++++++++++----------- process_bug.cgi | 15 +++- template/en/default/global/user-error.html.tmpl | 21 +++++ 9 files changed, 276 insertions(+), 75 deletions(-) (limited to 'attachment.cgi') diff --git a/Attachment.pm b/Attachment.pm index 322a3b2ba..ea5cd531c 100644 --- a/Attachment.pm +++ b/Attachment.pm @@ -48,10 +48,12 @@ sub new { bless($self, $class); &::PushGlobalSQLState(); - &::SendSQL("SELECT 1, description, bug_id FROM attachments " . + &::SendSQL("SELECT 1, description, bug_id, isprivate FROM attachments " . "WHERE attach_id = $id"); - ($self->{'exists'}, $self->{'summary'}, $self->{'bug_id'}) = - &::FetchSQLData(); + ($self->{'exists'}, + $self->{'summary'}, + $self->{'bug_id'}, + $self->{'isprivate'}) = &::FetchSQLData(); &::PopGlobalSQLState(); return $self; diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 322a3b2ba..ea5cd531c 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -48,10 +48,12 @@ sub new { bless($self, $class); &::PushGlobalSQLState(); - &::SendSQL("SELECT 1, description, bug_id FROM attachments " . + &::SendSQL("SELECT 1, description, bug_id, isprivate FROM attachments " . "WHERE attach_id = $id"); - ($self->{'exists'}, $self->{'summary'}, $self->{'bug_id'}) = - &::FetchSQLData(); + ($self->{'exists'}, + $self->{'summary'}, + $self->{'bug_id'}, + $self->{'isprivate'}) = &::FetchSQLData(); &::PopGlobalSQLState(); return $self; diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 41cc18071..a327f2922 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -33,9 +33,12 @@ use Bugzilla::FlagType; use Bugzilla::User; use Bugzilla::Config; use Bugzilla::Util; +use Bugzilla::Error; use Attachment; +use constant TABLES_ALREADY_LOCKED => 1; + # Note that this line doesn't actually import these variables for some reason, # so I have to use them as $::template and $::vars in the package code. use vars qw($template $vars); @@ -135,8 +138,8 @@ sub count { sub validate { # Validates fields containing flag modifications. - - my ($data) = @_; + + my ($data, $bug_id) = @_; # Get a list of flags to validate. Uses the "map" function # to extract flag IDs from form field names by matching fields @@ -152,13 +155,62 @@ sub validate { my $flag = get($id); $flag || &::ThrowCodeError("flag_nonexistent", { id => $id }); - # Don't bother validating flags the user didn't change. - next if $status eq $flag->{'status'}; - # Make sure the user chose a valid status. grep($status eq $_, qw(X + - ?)) || &::ThrowCodeError("flag_status_invalid", { id => $id , status => $status }); + + # Make sure the user didn't request the flag unless it's requestable. + if ($status eq '?' && !$flag->{type}->{is_requestable}) { + ThrowCodeError("flag_status_invalid", + { id => $id , status => $status }); + } + + # Make sure the requestee is authorized to access the bug. + # (and attachment, if this installation is using the "insider group" + # feature and the attachment is marked private). + if ($status eq '?' + && $flag->{type}->{is_requesteeble} + && trim($data->{"requestee-$id"})) + { + my $requestee_email = trim($data->{"requestee-$id"}); + if ($requestee_email ne $flag->{'requestee'}->{'email'}) { + # We know the requestee exists because we ran + # Bugzilla::User::match_field before getting here. + # ConfirmGroup makes sure their group settings + # are up-to-date or calls DeriveGroups to update them. + my $requestee_id = &::DBname_to_id($requestee_email); + &::ConfirmGroup($requestee_id); + + # Throw an error if the user can't see the bug. + if (!&::CanSeeBug($bug_id, $requestee_id)) + { + ThrowUserError("flag_requestee_unauthorized", + { flag_type => $flag->{'type'}, + requestee => + new Bugzilla::User($requestee_id), + bug_id => $bug_id, + attach_id => + $flag->{target}->{attachment}->{id} }); + } + + # Throw an error if the target is a private attachment and + # the requestee isn't in the group of insiders who can see it. + if ($flag->{target}->{attachment}->{exists} + && $data->{'isprivate'} + && &::Param("insidergroup") + && !&::UserInGroup(&::Param("insidergroup"), $requestee_id)) + { + ThrowUserError("flag_requestee_unauthorized_attachment", + { flag_type => $flag->{'type'}, + requestee => + new Bugzilla::User($requestee_id), + bug_id => $bug_id, + attach_id => + $flag->{target}->{attachment}->{id} }); + } + } + } } } @@ -457,14 +509,17 @@ sub GetBug { # Save the currently running query (if any) so we do not overwrite it. &::PushGlobalSQLState(); - &::SendSQL("SELECT 1, short_desc, product_id, component_id - FROM bugs - WHERE bug_id = $id"); + &::SendSQL("SELECT 1, short_desc, product_id, component_id, + COUNT(bug_group_map.group_id) + FROM bugs LEFT JOIN bug_group_map + ON (bugs.bug_id = bug_group_map.bug_id) + WHERE bugs.bug_id = $id + GROUP BY bugs.bug_id"); my $bug = { 'id' => $id }; ($bug->{'exists'}, $bug->{'summary'}, $bug->{'product_id'}, - $bug->{'component_id'}) = &::FetchSQLData(); + $bug->{'component_id'}, $bug->{'restricted'}) = &::FetchSQLData(); # Restore the previously running query (if any). &::PopGlobalSQLState(); @@ -504,6 +559,28 @@ sub notify { my ($flag, $template_file) = @_; + # If the target bug is restricted to one or more groups, then we need + # to make sure we don't send email about it to unauthorized users + # on the request type's CC: list, so we have to trawl the list for users + # not in those groups or email addresses that don't have an account. + if ($flag->{'target'}->{'bug'}->{'restricted'} + || $flag->{'target'}->{'attachment'}->{'isprivate'}) + { + my @new_cc_list; + foreach my $cc (split(/[, ]+/, $flag->{'type'}->{'cc_list'})) { + my $user_id = &::DBname_to_id($cc) || next; + # re-derive permissions if necessary + &::ConfirmGroup($user_id, TABLES_ALREADY_LOCKED); + next if $flag->{'target'}->{'bug'}->{'restricted'} + && !&::CanSeeBug($flag->{'target'}->{'bug'}->{'id'}, $user_id); + next if $flag->{'target'}->{'attachment'}->{'isprivate'} + && Param("insidergroup") + && !&::UserInGroup(Param("insidergroup"), $user_id); + push(@new_cc_list, $cc); + } + $flag->{'type'}->{'cc_list'} = join(", ", @new_cc_list); + } + $::vars->{'flag'} = $flag; my $message; diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index 2e272f67c..523f60190 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -32,6 +32,9 @@ package Bugzilla::FlagType; # Use Bugzilla's User module which contains utilities for handling users. use Bugzilla::User; +use Bugzilla::Error; +use Bugzilla::Util; + # Note! This module requires that its caller have said "require CGI.pl" # to import relevant functions from that script and its companion globals.pl. @@ -177,9 +180,9 @@ sub count { } sub validate { - my ($data) = @_; + my ($data, $bug_id, $attach_id) = @_; - # Get a list of flags types to validate. Uses the "map" function + # Get a list of flag types to validate. Uses the "map" function # to extract flag type IDs from form field names by matching columns # whose name looks like "flag_type-nnn", where "nnn" is the ID, # and returning just the ID portion of matching field names. @@ -192,14 +195,62 @@ sub validate { # Don't bother validating types the user didn't touch. next if $status eq "X"; - # Make sure the flag exists. - get($id) + # Make sure the flag type exists. + my $flag_type = get($id); + $flag_type || &::ThrowCodeError("flag_type_nonexistent", { id => $id }); # Make sure the value of the field is a valid status. grep($status eq $_, qw(X + - ?)) || &::ThrowCodeError("flag_status_invalid", { id => $id , status => $status }); + + # Make sure the user didn't request the flag unless it's requestable. + if ($status eq '?' && !$flag_type->{is_requestable}) { + ThrowCodeError("flag_status_invalid", + { id => $id , status => $status }); + } + + # Make sure the requestee is authorized to access the bug + # (and attachment, if this installation is using the "insider group" + # feature and the attachment is marked private). + if ($status eq '?' + && $flag_type->{is_requesteeble} + && trim($data->{"requestee_type-$id"})) + { + my $requestee_email = trim($data->{"requestee_type-$id"}); + my $requestee_id = &::DBname_to_id($requestee_email); + + # We know the requestee exists because we ran + # Bugzilla::User::match_field before getting here. + # ConfirmGroup makes sure their group settings + # are up-to-date or calls DeriveGroups to update them. + &::ConfirmGroup($requestee_id); + + # Throw an error if the user can't see the bug. + if (!&::CanSeeBug($bug_id, $requestee_id)) + { + ThrowUserError("flag_requestee_unauthorized", + { flag_type => $flag_type, + requestee => new Bugzilla::User($requestee_id), + bug_id => $bug_id, + attach_id => $attach_id }); + } + + # Throw an error if the target is a private attachment and + # the requestee isn't in the group of insiders who can see it. + if ($attach_id + && &::Param("insidergroup") + && $data->{'isprivate'} + && !&::UserInGroup(&::Param("insidergroup"), $requestee_id)) + { + ThrowUserError("flag_requestee_unauthorized_attachment", + { flag_type => $flag_type, + requestee => new Bugzilla::User($requestee_id), + bug_id => $bug_id, + attach_id => $attach_id }); + } + } } } diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index 5aecb5ad9..511ba2592 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -129,8 +129,10 @@ sub min { sub trim { my ($str) = @_; - $str =~ s/^\s+//g; - $str =~ s/\s+$//g; + if ($str) { + $str =~ s/^\s+//g; + $str =~ s/\s+$//g; + } return $str; } diff --git a/attachment.cgi b/attachment.cgi index 51a154645..621477ed5 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -52,6 +52,17 @@ ConnectToDatabase(); # Check whether or not the user is logged in and, if so, set the $::userid quietly_check_login(); +# The ID of the bug to which the attachment is attached. Gets set +# by validateID() (which validates the attachment ID, not the bug ID, but has +# to check if the user is authorized to access this attachment) and is used +# by Flag:: and FlagType::validate() to ensure the requestee (if any) for a +# requested flag is authorized to see the bug in question. Note: This should +# really be defined just above validateID() itself, but it's used in the main +# body of the script before that function is defined, so we define it up here +# instead. We should move the validation into each function and then move this +# to just above validateID(). +my $bugid; + ################################################################################ # Main Body Execution ################################################################################ @@ -113,10 +124,15 @@ elsif ($action eq "update") validateContentType() unless $::FORM{'ispatch'}; validateIsObsolete(); validatePrivate(); + + # The order of these function calls is important, as both Flag::validate + # and FlagType::validate assume User::match_field has ensured that the values + # in the requestee fields are legitimate user email addresses. Bugzilla::User::match_field({ '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } }); - Bugzilla::Flag::validate(\%::FORM); - Bugzilla::FlagType::validate(\%::FORM); + Bugzilla::Flag::validate(\%::FORM, $bugid); + Bugzilla::FlagType::validate(\%::FORM, $bugid, $::FORM{'id'}); + update(); } else @@ -146,7 +162,7 @@ sub validateID || ThrowUserError("invalid_attach_id"); # Make sure the user is authorized to access this attachment's bug. - my ($bugid, $isprivate) = FetchSQLData(); + ($bugid, my $isprivate) = FetchSQLData(); ValidateBugID($bugid); if (($isprivate > 0 ) && Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { ThrowUserError("attachment_access_denied"); @@ -677,7 +693,16 @@ sub update SendSQL("LOCK TABLES attachments WRITE , flags WRITE , " . "flagtypes READ , fielddefs READ , bugs_activity WRITE, " . "flaginclusions AS i READ, flagexclusions AS e READ, " . - "bugs READ, profiles READ"); + # cc, bug_group_map, user_group_map, and groups are in here so we + # can check the permissions of flag requestees and email addresses + # on the flag type cc: lists via the ConfirmGroup and CanSeeBug + # function calls in Flag::notify. group_group_map is in here in case + # ConfirmGroup needs to call DeriveGroup. profiles and user_group_map + # would be READ locks instead of WRITE locks if it weren't for + # DeriveGroup, which needs to write to those tables. + "bugs READ, profiles WRITE, " . + "cc READ, bug_group_map READ, user_group_map WRITE, " . + "group_group_map READ, groups READ"); # Get a copy of the attachment record before we make changes # so we can record those changes in the activity table. diff --git a/globals.pl b/globals.pl index 911f99278..4caaa3f84 100644 --- a/globals.pl +++ b/globals.pl @@ -549,9 +549,15 @@ sub CanEnterProduct { # # This function returns an alphabetical list of product names to which -# the user can enter bugs. +# the user can enter bugs. If the $by_id parameter is true, also retrieves IDs +# and pushes them onto the list as id, name [, id, name...] for easy slurping +# into a hash by the calling code. sub GetSelectableProducts { - my $query = "SELECT name " . + my ($by_id) = @_; + + my $extra_sql = $by_id ? "id, " : ""; + + my $query = "SELECT $extra_sql name " . "FROM products " . "LEFT JOIN group_control_map " . "ON group_control_map.product_id = products.id "; @@ -570,9 +576,7 @@ sub GetSelectableProducts { PushGlobalSQLState(); SendSQL($query); my @products = (); - while (MoreSQLData()) { - push @products,FetchOneColumn(); - } + push(@products, FetchSQLData()) while MoreSQLData(); PopGlobalSQLState(); return (@products); } @@ -580,50 +584,50 @@ sub GetSelectableProducts { # GetSelectableProductHash # returns a hash containing # legal_products => an enterable product list -# legal_components => the list of components of enterable products -# components => a hash of component lists for each enterable product +# legal_(components|versions|milestones) => +# the list of components, versions, and milestones of enterable products +# (components|versions|milestones)_by_product +# => a hash of component lists for each enterable product +# Milestones only get returned if the usetargetmilestones parameter is set. sub GetSelectableProductHash { - my $query = "SELECT products.name, components.name " . - "FROM products " . - "LEFT JOIN components " . - "ON components.product_id = products.id " . - "LEFT JOIN group_control_map " . - "ON group_control_map.product_id = products.id "; - if (Param('useentrygroupdefault')) { - $query .= "AND group_control_map.entry != 0 "; - } else { - $query .= "AND group_control_map.membercontrol = " . - CONTROLMAPMANDATORY . " "; - } - if ((defined @{$::vars->{user}{groupids}}) - && (@{$::vars->{user}{groupids}} > 0)) { - $query .= "AND group_id NOT IN(" . - join(',', @{$::vars->{user}{groupids}}) . ") "; - } - $query .= "WHERE group_id IS NULL " . - "ORDER BY products.name, components.name"; + # The hash of selectable products and their attributes that gets returned + # at the end of this function. + my $selectables = {}; + + my %products = GetSelectableProducts(1); + + $selectables->{legal_products} = [sort values %products]; + + # Run queries that retrieve the list of components, versions, + # and target milestones (if used) for the selectable products. + my @tables = qw(components versions); + push(@tables, 'milestones') if Param('usetargetmilestone'); + PushGlobalSQLState(); - SendSQL($query); - my @products = (); - my %components = (); - my %components_by_product = (); - while (MoreSQLData()) { - my ($product, $component) = FetchSQLData(); - if (!grep($_ eq $product, @products)) { - push @products, $product; - } - if ($component) { - $components{$component} = 1; - push @{$components_by_product{$product}}, $component; + foreach my $table (@tables) { + # Why oh why can't we standardize on these names?!? + my $fld = ($table eq "components" ? "name" : "value"); + + my $query = "SELECT $fld, product_id FROM $table WHERE product_id IN " . + "(" . join(",", keys %products) . ") ORDER BY $fld"; + SendSQL($query); + + my %values; + my %values_by_product; + + while (MoreSQLData()) { + my ($name, $product_id) = FetchSQLData(); + next unless $name; + $values{$name} = 1; + push @{$values_by_product{$products{$product_id}}}, $name; } + + $selectables->{"legal_$table"} = [sort keys %values]; + $selectables->{"${table}_by_product"} = \%values_by_product; } PopGlobalSQLState(); - my @componentlist = (sort keys %components); - return { - legal_products => \@products, - legal_components => \@componentlist, - components => \%components_by_product, - }; + + return $selectables; } @@ -724,24 +728,28 @@ sub Crypt { # Permissions must be rederived if ANY groups have a last_changed newer # than the profiles.refreshed_when value. sub ConfirmGroup { - my ($user) = (@_); + my ($user, $locked) = (@_); PushGlobalSQLState(); SendSQL("SELECT userid FROM profiles, groups WHERE userid = $user " . "AND profiles.refreshed_when <= groups.last_changed "); my $ret = FetchSQLData(); PopGlobalSQLState(); if ($ret) { - DeriveGroup($user); + DeriveGroup($user, $locked); } } # DeriveGroup removes and rederives all derived group permissions for -# the specified user. +# the specified user. If $locked is true, Bugzilla has already locked +# the necessary tables as part of a larger transaction, so this function +# shouldn't lock them again (since then tables not part of this function's +# lock will get unlocked). sub DeriveGroup { - my ($user) = (@_); + my ($user, $locked) = (@_); PushGlobalSQLState(); - SendSQL("LOCK TABLES profiles WRITE, user_group_map WRITE, group_group_map READ, groups READ"); + SendSQL("LOCK TABLES profiles WRITE, user_group_map WRITE, group_group_map READ, groups READ") + unless $locked; # avoid races, we are only as up to date as the BEGINNING of this process SendSQL("SELECT login_name, NOW() FROM profiles WHERE userid = $user"); diff --git a/process_bug.cgi b/process_bug.cgi index 09b45cf92..83d601d33 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -91,12 +91,21 @@ scalar(@idlist) || ThrowUserError("no_bugs_chosen"); # do a match on the fields if applicable +# The order of these function calls is important, as both Flag::validate +# and FlagType::validate assume User::match_field has ensured that the values +# in the requestee fields are legitimate user email addresses. &Bugzilla::User::match_field({ 'qa_contact' => { 'type' => 'single' }, 'newcc' => { 'type' => 'multi' }, 'assigned_to' => { 'type' => 'single' }, '^requestee(_type)?-(\d+)$' => { 'type' => 'single' }, }); +# Validate flags, but only if the user is changing a single bug, +# since the multi-change form doesn't include flag changes. +if (defined $::FORM{'id'}) { + Bugzilla::Flag::validate(\%::FORM, $::FORM{'id'}); + Bugzilla::FlagType::validate(\%::FORM, $::FORM{'id'}); +} # If we are duping bugs, let's also make sure that we can change # the original. This takes care of issue A on bug 96085. @@ -1080,7 +1089,11 @@ foreach my $id (@idlist) { "products READ, components READ, " . "keywords $write, longdescs $write, fielddefs $write, " . "bug_group_map $write, flags $write, duplicates $write," . - "user_group_map READ, flagtypes READ, " . + # user_group_map would be a READ lock except that Flag::process + # may call Flag::notify, which calls ConfirmGroup, which might + # call DeriveGroup, which wants a WRITE lock on that table. + # group_group_map is in here at all because DeriveGroups needs it. + "user_group_map $write, group_group_map READ, flagtypes READ, " . "flaginclusions AS i READ, flagexclusions AS e READ, " . "keyworddefs READ, groups READ, attachments READ, " . "group_control_map AS oldcontrolmap READ, " . diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 934c0511f..1aaa581b6 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -180,6 +180,27 @@ format like JPG or PNG, or put it elsewhere on the web and link to it from the bug's URL field or in a comment on the bug. + [% ELSIF error == "flag_requestee_unauthorized" %] + [% title = "Flag Requestee Not Authorized" %] + + You asked [% requestee.identity FILTER html %] + for [% flag_type.name FILTER html %] on bug [% bug_id -%] + [% IF attach_id %], attachment [% attach_id %][% END %], but that bug + has been restricted to users in certain groups, and the user you asked + isn't in all the groups to which the bug has been restricted. + Please choose someone else to ask, or make the bug accessible to users + on its CC: list and add that user to the list. + + [% ELSIF error == "flag_requestee_unauthorized_attachment" %] + [% title = "Flag Requestee Not Authorized" %] + + You asked [% requestee.identity FILTER html %] + for [% flag_type.name FILTER html %] on bug [% bug_id %], + attachment [% attach_id %], but that attachment is restricted to users + in the [% Param("insidergroup") FILTER html %] group, and the user + you asked isn't in that group. Please choose someone else to ask, + or ask an administrator to add the user to the group. + [% ELSIF error == "flag_type_cc_list_invalid" %] [% title = "Flag Type CC List Invalid" %] The CC list [% cc_list FILTER html %] must be less than 200 characters long. -- cgit v1.2.3-65-gdbad From 9488a8906592564ec2e7601041f3ea5484cde3cc Mon Sep 17 00:00:00 2001 From: "bbaetz%acm.org" <> Date: Mon, 5 May 2003 08:15:19 +0000 Subject: Bug 201816 - use CGI.pm for header output r=joel, a=justdave --- Bugzilla/Auth/CGI.pm | 21 ++++-- Bugzilla/CGI.pm | 89 ++++++++++++++++++++----- Bugzilla/Constants.pm | 13 +++- Bugzilla/Error.pm | 3 +- Bugzilla/Flag.pm | 2 +- Bugzilla/User.pm | 2 +- CGI.pl | 4 +- attachment.cgi | 27 ++++---- buglist.cgi | 66 ++++++++++-------- checksetup.pl | 20 +----- colchange.cgi | 21 ++++-- createaccount.cgi | 11 +-- describecomponents.cgi | 8 ++- describekeywords.cgi | 6 +- doeditparams.cgi | 5 +- duplicates.cgi | 11 +-- editcomponents.cgi | 2 +- editflagtypes.cgi | 17 ++--- editgroups.cgi | 2 +- editkeywords.cgi | 2 +- editmilestones.cgi | 2 +- editparams.cgi | 2 +- editproducts.cgi | 2 +- editusers.cgi | 2 +- editversions.cgi | 2 +- enter_bug.cgi | 7 +- globals.pl | 3 +- importxml.pl | 4 +- index.cgi | 4 +- long_list.cgi | 7 +- move.pl | 9 ++- page.cgi | 9 ++- post_bug.cgi | 14 ++-- process_bug.cgi | 7 +- query.cgi | 11 ++- queryhelp.cgi | 2 +- quips.cgi | 4 +- relogin.cgi | 14 ++-- report.cgi | 16 +++-- reports.cgi | 9 ++- request.cgi | 2 +- show_activity.cgi | 2 +- show_bug.cgi | 11 ++- showattachment.cgi | 18 +++-- showdependencygraph.cgi | 5 +- showdependencytree.cgi | 4 +- sidebar.cgi | 7 +- template/en/default/global/code-error.html.tmpl | 5 -- token.cgi | 16 +++-- userprefs.cgi | 6 +- votes.cgi | 19 ++++-- 51 files changed, 352 insertions(+), 205 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Auth/CGI.pm b/Bugzilla/Auth/CGI.pm index 034013bda..3588b7037 100644 --- a/Bugzilla/Auth/CGI.pm +++ b/Bugzilla/Auth/CGI.pm @@ -70,9 +70,13 @@ sub login { undef, $userid, $ipaddr); my $logincookie = $dbh->selectrow_array("SELECT LAST_INSERT_ID()"); - my $cookiepath = Param("cookiepath"); - print "Set-Cookie: Bugzilla_login=$userid ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; - print "Set-Cookie: Bugzilla_logincookie=$logincookie ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; + + $cgi->send_cookie(-name => 'Bugzilla_login', + -value => $userid, + -expires => 'Fri, 01-Jan-2038 00:00:00 GMT'); + $cgi->send_cookie(-name => 'Bugzilla_logincookie', + -value => $logincookie, + -expires => 'Fri, 01-Jan-2038 00:00:00 GMT'); # compat code. The cookie value is used for logouts, and that # isn't generic yet. @@ -120,7 +124,7 @@ sub login { if ($authres == AUTH_NODATA && $type == LOGIN_REQUIRED) { # Throw up the login page - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); my $template = Bugzilla->template; $template->process("account/auth/login.html.tmpl", @@ -152,9 +156,12 @@ sub login { # The account may be disabled if ($authres == AUTH_DISABLED) { # Clear the cookie - my $cookiepath = Param("cookiepath"); - print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n"; - print "Set-Cookie: Bugzilla_logincookie= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n"; + + $cgi->send_cookie(-name => 'Bugzilla_login', + -expires => "Tue, 15-Sep-1998 21:49:00 GMT"); + $cgi->send_cookie(-name => 'Bugzilla_logincookie', + -expires => "Tue, 15-Sep-1998 21:49:00 GMT"); + # and throw a user error &::ThrowUserError("account_disabled", {'disabled_reason' => $extra}); diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 3c00ed347..e87c89a5e 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -23,11 +23,12 @@ use strict; package Bugzilla::CGI; -use CGI qw(-no_xhtml -oldstyle_urls :private_tempfiles); +use CGI qw(-no_xhtml -oldstyle_urls :private_tempfiles :unique_headers); use base qw(CGI); use Bugzilla::Util; +use Bugzilla::Config; # We need to disable output buffering - see bug 179174 $| = 1; @@ -44,6 +45,9 @@ sub new { my $self = $class->SUPER::new(@args); + # Make sure that we don't send any charset headers + $self->charset(''); + # Check for errors # All of the Bugzilla code wants to do this, so do it here instead of # in each script @@ -62,20 +66,18 @@ sub new { # multipart requests, and so should never happen unless there is a # browser bug. - # Using CGI.pm to do this means that ThrowCodeError prints the - # content-type again... - #print $self->header(-status => $err); - print "Status: $err\n"; - - my $vars = {}; - if ($err =~ m/(\d{3})\s(.*)/) { - $vars->{http_error_code} = $1; - $vars->{http_error_string} = $2; - } else { - $vars->{http_error_string} = $err; - } - - &::ThrowCodeError("cgi_error", $vars); + print $self->header(-status => $err); + + # ThrowCodeError wants to print the header, so it grabs Bugzilla->cgi + # which creates a new Bugzilla::CGI object, which fails again, which + # ends up here, and calls ThrowCodeError, and then recurses forever. + # So don't use it. + # In fact, we can't use templates at all, because we need a CGI object + # to determine the template lang as well as the current url (from the + # template) + # Since this is an internal error which indicates a severe browser bug, + # just die. + die "CGI parsing error: $err"; } return $self; @@ -105,6 +107,46 @@ sub canonicalise_query { return join("&", @parameters); } +# CGI.pm makes this nph, but apache doesn't like that +sub multipart_init { + my $self = shift; + + unshift(@_, '-nph' => undef); + + return $self->SUPER::multipart_init(@_); +} + +sub cookie { + my $self = shift; + + # Add the default path in, but only if we're fetching stuff + # (This test fails for |$cgi->cookie(-name=>'x')| which _is_ meant to + # fetch, but thats an ugly notation for the fetch case which we shouldn't + # be using) + unshift(@_, '-path' => Param('cookiepath')) if scalar(@_)>1; + + return $self->SUPER::cookie(@_); +} + +# The various parts of Bugzilla which create cookies don't want to have to +# pass them arround to all of the callers. Instead, store them locally here, +# and then output as required from |headers|. +# This is done instead of just printing the result from the script, because +# we need to use |$r->header_out| under mod_perl (which is what CGI.pm +# does, and we need to match, plus if we don't |print| anything, we can turn +# off mod_perl/Apache's header parsing for a small perf gain) +sub send_cookie { + my $self = shift; + + my $cookie = $self->cookie(@_); + + # XXX - mod_perl + print "Set-Cookie: $cookie\r\n"; + + return; +} + + 1; __END__ @@ -149,4 +191,21 @@ I also includes additional functions. This returns a sorted string of the parameters, suitable for use in a url. Values in C<@exclude> are not included in the result. +=item C + +Identical to the CGI.pm C routine, except that the cookie path is +automatically added. + +=item C + +This routine is identical to CGI.pm's C routine, except that the cookie +is sent to the browser, rather than returned. This should be used by all +Bugzilla code (instead of C or the C<-cookie> argument to C
), +so that under mod_perl the headers can be sent correctly, using C or +the mod_perl APIs as appropriate. + =back + +=head1 SEE ALSO + +L, L diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 5e6b5365d..a1bf74ba0 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -47,7 +47,8 @@ use base qw(Exporter); LOGIN_NORMAL LOGIN_REQUIRED ); - + +@Bugzilla::Constants::EXPORT_OK = qw(contenttypes); # CONSTANTS # @@ -94,4 +95,14 @@ use constant LOGIN_OPTIONAL => 0; use constant LOGIN_NORMAL => 1; use constant LOGIN_REQUIRED => 2; +use constant contenttypes => + { + "html" => "text/html" , + "rdf" => "application/xml" , + "xml" => "text/xml" , + "js" => "application/x-javascript" , + "csv" => "text/plain" , + "png" => "image/png" , + }; + 1; diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index 64314121a..485646274 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -39,8 +39,7 @@ sub ThrowUserError { Bugzilla->dbh->do("UNLOCK TABLES") if $unlock_tables; - # XXX - mod_perl - print "Content-type: text/html\n\n" if !$::vars->{'header_done'}; + print Bugzilla->cgi->header(); my $template = Bugzilla->template; $template->process("global/user-error.html.tmpl", $vars) diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index a327f2922..f8eb8a4a4 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -587,7 +587,7 @@ sub notify { my $rv = $::template->process($template_file, $::vars, \$message); if (!$rv) { - print "Content-Type: text/html\n\n" unless $::vars->{'header_done'}; + Bugzilla->cgi->header(); &::ThrowTemplateError($::template->error()); } diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 46f520b77..fde9d336b 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -366,7 +366,7 @@ sub match_field { $vars->{'matches'} = $matches; # matches that were made $vars->{'matchsuccess'} = $matchsuccess; # continue or fail - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); $::template->process("global/confirm-user-match.html.tmpl", $vars) || &::ThrowTemplateError($::template->error()); diff --git a/CGI.pl b/CGI.pl index 74b81c9da..1a6d7c93a 100644 --- a/CGI.pl +++ b/CGI.pl @@ -59,7 +59,7 @@ if (Param("shutdownhtml") && $0 !~ m:[\\/](do)?editparams.cgi$:) { $::vars->{'message'} = "shutdown"; # Return the appropriate HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return an HTML message about the downtime. $::template->process("global/message.html.tmpl", $::vars) @@ -320,7 +320,7 @@ sub ThrowCodeError { $vars->{'variables'} = $extra_vars; } - print "Content-type: text/html\n\n" if !$vars->{'header_done'}; + print Bugzilla->cgi->header(); $template->process("global/code-error.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/attachment.cgi b/attachment.cgi index 621477ed5..26892181f 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -33,7 +33,6 @@ use strict; use lib qw(.); use vars qw( - $cgi $template $vars ); @@ -63,6 +62,8 @@ quietly_check_login(); # to just above validateID(). my $bugid; +my $cgi = Bugzilla->cgi; + ################################################################################ # Main Body Execution ################################################################################ @@ -399,11 +400,12 @@ sub view # Return the appropriate HTTP response headers. $filename =~ s/^.*[\/\\]//; my $filesize = length($thedata); - print qq{Content-Type: $contenttype; name="$filename"\n}; - print qq{Content-Disposition: inline; filename=$filename\n}; - print qq{Content-Length: $filesize\n}; - print qq{\n$thedata}; + print Bugzilla->cgi->header(-type=>"$contenttype; name=\"$filename\"", + -content_disposition=> "inline; filename=$filename\n", + -content_length => $filesize); + + print $thedata; } @@ -450,8 +452,7 @@ sub viewall $vars->{'bugsummary'} = $bugsummary; $vars->{'GetBugLink'} = \&GetBugLink; - # Return the appropriate HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/show-multiple.html.tmpl", $vars) @@ -495,8 +496,7 @@ sub enter $vars->{'bugsummary'} = $bugsummary; $vars->{'GetBugLink'} = \&GetBugLink; - # Return the appropriate HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/create.html.tmpl", $vars) @@ -604,8 +604,7 @@ sub insert $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; $vars->{'contenttype'} = $::FORM{'contenttype'}; - # Return the appropriate HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/created.html.tmpl", $vars) @@ -667,8 +666,7 @@ sub edit $vars->{'attachments'} = \@bugattachments; $vars->{'GetBugLink'} = \&GetBugLink; - # Return the appropriate HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/edit.html.tmpl", $vars) @@ -815,8 +813,7 @@ sub update $vars->{'attachid'} = $::FORM{'id'}; $vars->{'bugid'} = $bugid; - # Return the appropriate HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/updated.html.tmpl", $vars) diff --git a/buglist.cgi b/buglist.cgi index 4acd5d55e..06c00db93 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -33,7 +33,7 @@ use strict; use lib qw(.); -use vars qw($cgi $template $vars); +use vars qw($template $vars); use Bugzilla; use Bugzilla::Search; @@ -56,10 +56,12 @@ use vars qw($db_name $userid @versions); +my $cgi = Bugzilla->cgi; + if (length($::buffer) == 0) { - print "Refresh: 10; URL=query.cgi\n"; + print $cgi->header(-refresh=> '10; URL=query.cgi'); ThrowUserError("buglist_parameters_required"); -} +} ConnectToDatabase(); @@ -131,8 +133,7 @@ if ($::FORM{'regetlastlist'}) { if ($::buffer =~ /&cmd-/) { my $url = "query.cgi?$::buffer#chart"; - print "Refresh: 0; URL=$url\n"; - print "Content-Type: text/html\n\n"; + print $cgi->redirect(-location => $url); # Generate and return the UI (HTML page) from the appropriate template. $vars->{'message'} = "buglist_adding_field"; $vars->{'url'} = $url; @@ -257,8 +258,7 @@ if ($::FORM{'cmdtype'} eq "dorem") { } elsif ($::FORM{'remaction'} eq "load") { my $url = "query.cgi?" . LookupNamedQuery($::FORM{"namedcmd"}); - print "Refresh: 0; URL=$url\n"; - print "Content-Type: text/html\n\n"; + print $cgi->redirect(-location=>$url); # Generate and return the UI (HTML page) from the appropriate template. $vars->{'message'} = "buglist_load_named_query"; $vars->{'namedcmd'} = $::FORM{'namedcmd'}; @@ -282,7 +282,7 @@ if ($::FORM{'cmdtype'} eq "dorem") { $count++; } - print "Content-Type: text/html\n\n"; + print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $vars->{'message'} = "buglist_query_gone"; $vars->{'namedcmd'} = $::FORM{'namedcmd'}; @@ -535,8 +535,8 @@ if ($order) { if (!grep($fragment =~ /^\Q$_\E(\s+(asc|desc))?$/, @columnnames)) { $vars->{'fragment'} = $fragment; if ($order_from_cookie) { - my $cookiepath = Param("cookiepath"); - print "Set-Cookie: LASTORDER= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n"; + $cgi->send_cookie(-name => 'LASTORDER', + -expires => 'Tue, 15-Sep-1998 21:49:00 GMT'); ThrowCodeError("invalid_column_name_cookie"); } else { @@ -618,15 +618,15 @@ $query .= " ORDER BY $db_order " if ($order); # Time to use server push to display an interim message to the user until # the query completes and we can display the bug list. if ($serverpush) { - # Generate HTTP headers. - print "Content-Disposition: inline; filename=$filename\n"; - print "Content-Type: multipart/x-mixed-replace;boundary=thisrandomstring\n\n"; - print "--thisrandomstring\n"; - print "Content-Type: text/html\n\n"; + print $cgi->multipart_init(-content_disposition => "inline; filename=$filename"); + + print $cgi->multipart_start(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("list/server-push.html.tmpl", $vars) || ThrowTemplateError($template->error()); + + print $cgi->multipart_end(); } # Connect to the shadow database if this installation is using one to improve @@ -800,39 +800,47 @@ if ($dotweak) { # HTTP Header Generation ################################################################################ -# If we are doing server push, output a separator string. -print "\n--thisrandomstring\n" if $serverpush; - # Generate HTTP headers -# Suggest a name for the bug list if the user wants to save it as a file. -# If we are doing server push, then we did this already in the HTTP headers -# that started the server push, so we don't have to do it again here. -print "Content-Disposition: inline; filename=$filename\n" unless $serverpush; +my $contenttype; if ($format->{'extension'} eq "html") { my $cookiepath = Param("cookiepath"); - print "Content-Type: text/html\n"; if ($order) { my $qorder = url_quote($order); - print "Set-Cookie: LASTORDER=$qorder ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; + $cgi->send_cookie(-name => 'LASTORDER', + -value => $qorder, + -expires => 'Fri, 01-Jan-2038 00:00:00 GMT'); } my $bugids = join(":", @bugidlist); # See also Bug 111999 if (length($bugids) < 4000) { - print "Set-Cookie: BUGLIST=$bugids ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; + $cgi->send_cookie(-name => 'BUGLIST', + -value => $bugids, + -expires => 'Fri, 01-Jan-2038 00:00:00 GMT'); } else { - print "Set-Cookie: BUGLIST= ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; + $cgi->send_cookie(-name => 'BUGLIST', + -expires => 'Tue, 15-Sep-1998 21:49:00 GMT'); $vars->{'toolong'} = 1; } + + $contenttype = "text/html"; } else { - print "Content-Type: $format->{'ctype'}\n"; + $contenttype = $format->{'ctype'}; } -print "\n"; # end HTTP headers +if ($serverpush) { + print $cgi->multipart_start(-type=>$contenttype); +} else { + # Suggest a name for the bug list if the user wants to save it as a file. + # If we are doing server push, then we did this already in the HTTP headers + # that started the server push, so we don't have to do it again here. + print $cgi->header(-type => $contenttype, + -content_disposition => "inline; filename=$filename"); +} ################################################################################ @@ -848,4 +856,4 @@ $template->process($format->{'template'}, $vars) # Script Conclusion ################################################################################ -print "\n--thisrandomstring--\n" if $serverpush; +print $cgi->multipart_final() if $serverpush; diff --git a/checksetup.pl b/checksetup.pl index 4173c67d8..451078863 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -211,7 +211,7 @@ my $modules = [ }, { name => 'CGI', - version => '2.88' + version => '2.93' }, { name => 'Data::Dumper', @@ -587,24 +587,6 @@ LocalVar('platforms', ' ); '); - - -LocalVar('contenttypes', ' -# -# The types of content that template files can generate, indexed by file extension. -# -$contenttypes = { - "html" => "text/html" , - "rdf" => "application/xml" , - "xml" => "text/xml" , - "js" => "application/x-javascript" , - "csv" => "text/plain" , - "png" => "image/png" , -}; -'); - - - if ($newstuff ne "") { print "\nThis version of Bugzilla contains some variables that you may want\n", "to change and adapt to your local settings. Please edit the file\n", diff --git a/colchange.cgi b/colchange.cgi index 5e28a4622..2ff2f3fee 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -32,6 +32,8 @@ use vars qw( $vars ); +use Bugzilla; + require "CGI.pl"; ConnectToDatabase(); @@ -39,6 +41,8 @@ quietly_check_login(); GetVersionTable(); +my $cgi = Bugzilla->cgi; + # The master list not only says what fields are possible, but what order # they get displayed in. my @masterlist = ("opendate", "changeddate", "bug_severity", "priority", @@ -87,12 +91,15 @@ if (defined $::FORM{'rememberedquery'}) { } my $list = join(" ", @collist); my $urlbase = Param("urlbase"); - my $cookiepath = Param("cookiepath"); - - print "Set-Cookie: COLUMNLIST=$list ; path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; - print "Set-Cookie: SPLITHEADER=$::FORM{'splitheader'} ; path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; - print "Refresh: 0; URL=buglist.cgi?$::FORM{'rememberedquery'}\n"; - print "Content-type: text/html\n\n"; + + $cgi->send_cookie(-name => 'COLUMNLIST', + -value => $list, + -expires => 'Fri, 01-Jan-2038 00:00:00 GMT'); + $cgi->send_cookie(-name => 'SPLITHEADER', + -value => $::FORM{'splitheader'}, + -expires => 'Fri, 01-Jan-2038 00:00:00 GMT'); + + print $cgi->redirect("buglist.cgi?$::FORM{'rememberedquery'}"); $vars->{'message'} = "change_columns"; $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -111,6 +118,6 @@ $vars->{'splitheader'} = $::COOKIE{'SPLITHEADER'} ? 1 : 0; $vars->{'buffer'} = $::buffer; # Generate and return the UI (HTML page) from the appropriate template. -print "Content-type: text/html\n\n"; +print $cgi->header(); $template->process("list/change-columns.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/createaccount.cgi b/createaccount.cgi index dec8e716c..cce598ac9 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -47,13 +47,16 @@ unless (Bugzilla::Auth->can_edit) { ThrowUserError("auth_cant_create_account"); } +my $cgi = Bugzilla->cgi; + # Clear out the login cookies. Make people log in again if they create an # account; otherwise, they'll probably get confused. -my $cookiepath = Param("cookiepath"); -print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT -Set-Cookie: Bugzilla_logincookie= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n"; +$cgi->send_cookie(-name => 'Bugzilla_login', + -expires => 'Tue, 15-Sep-1998 21:49:00 GMT'); +$cgi->send_cookie(-name => 'Bugzilla_logincookie', + -expires => 'Tue, 15-Sep-1998 21:49:00 GMT'); -print "Content-Type: text/html\n\n"; +print $cgi->header(); my $login = $::FORM{'login'}; diff --git a/describecomponents.cgi b/describecomponents.cgi index bdb824b82..ff7f46ac8 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -31,6 +31,8 @@ use strict; use lib qw(.); +use Bugzilla; + require "CGI.pl"; ConnectToDatabase(); @@ -38,6 +40,8 @@ quietly_check_login(); GetVersionTable(); +my $cgi = Bugzilla->cgi; + if (!defined $::FORM{'product'}) { # Reference to a subset of %::proddesc, which the user is allowed to see my %products; @@ -63,7 +67,7 @@ if (!defined $::FORM{'product'}) { $::vars->{'proddesc'} = \%products; $::vars->{'target'} = "describecomponents.cgi"; - print "Content-type: text/html\n\n"; + print $cgi->header(); $::template->process("global/choose-product.html.tmpl", $::vars) || ThrowTemplateError($::template->error()); exit; @@ -118,7 +122,7 @@ while (MoreSQLData()) { $::vars->{'product'} = $product; $::vars->{'components'} = \@components; -print "Content-type: text/html\n\n"; +print $cgi->header(); $::template->process("reports/components.html.tmpl", $::vars) || ThrowTemplateError($::template->error()); diff --git a/describekeywords.cgi b/describekeywords.cgi index 0ff538b63..60c5a9fd8 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -24,6 +24,8 @@ use strict; use lib "."; +use Bugzilla; + require "CGI.pl"; # Use the global template variables. @@ -33,6 +35,8 @@ ConnectToDatabase(); quietly_check_login(); +my $cgi = Bugzilla->cgi; + SendSQL("SELECT keyworddefs.name, keyworddefs.description, COUNT(keywords.bug_id) FROM keyworddefs LEFT JOIN keywords ON keyworddefs.id=keywords.keywordid @@ -52,6 +56,6 @@ while (MoreSQLData()) { $vars->{'keywords'} = \@keywords; $vars->{'caneditkeywords'} = UserInGroup("editkeywords"); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); $template->process("reports/keywords.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/doeditparams.cgi b/doeditparams.cgi index 48c39bc7a..cba06dd29 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -25,6 +25,7 @@ use strict; use lib qw(.); +use Bugzilla; use Bugzilla::Config qw(:DEFAULT :admin); require "CGI.pl"; @@ -34,7 +35,9 @@ use vars %::MFORM; ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +my $cgi = Bugzilla->cgi; + +print $cgi->header(); if (!UserInGroup("tweakparams")) { print "

Sorry, you aren't a member of the 'tweakparams' group.

\n"; diff --git a/duplicates.cgi b/duplicates.cgi index dc65ef502..27333cbab 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -36,15 +36,18 @@ use vars qw($buffer); use Bugzilla; use Bugzilla::Search; -use Bugzilla::CGI; + +my $cgi = Bugzilla->cgi; # Go directly to the XUL version of the duplicates report (duplicates.xul) # if the user specified ctype=xul. Adds params if they exist, and directs # the user to a signed copy of the script in duplicates.jar if it exists. if ($::FORM{'ctype'} && $::FORM{'ctype'} eq "xul") { my $params = CanonicaliseParams($::buffer, ["format", "ctype"]); - print "Location: " . (-e "duplicates.jar" ? "duplicates.jar!/" : "") . + my $url = (-e "duplicates.jar" ? "duplicates.jar!/" : "") . "duplicates.xul" . ($params ? "?$params" : "") . "\n\n"; + + print $cgi->redirect($url); exit; } @@ -261,8 +264,8 @@ $vars->{'products'} = \@::legal_product; my $format = GetFormat("reports/duplicates", $::FORM{'format'}, $::FORM{'ctype'}); - -print "Content-Type: $format->{'ctype'}\n\n"; + +print $cgi->header($format->{'ctype'}); # Generate and return the UI (HTML page) from the appropriate template. $template->process($format->{'template'}, $vars) diff --git a/editcomponents.cgi b/editcomponents.cgi index 35c6426b2..74e0debe8 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -191,7 +191,7 @@ sub PutTrailer (@) ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); unless (UserInGroup("editcomponents")) { PutHeader("Not allowed"); diff --git a/editflagtypes.cgi b/editflagtypes.cgi index d7794ff93..711828b6a 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -35,6 +35,7 @@ require "CGI.pl"; ConnectToDatabase(); # Use Bugzilla's flag modules for handling flag types. +use Bugzilla; use Bugzilla::Flag; use Bugzilla::FlagType; @@ -94,7 +95,7 @@ sub list { Bugzilla::FlagType::match({ 'target_type' => 'attachment' }, 1); # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("admin/flag-type/list.html.tmpl", $vars) @@ -138,7 +139,7 @@ sub edit { } # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("admin/flag-type/edit.html.tmpl", $vars) @@ -189,7 +190,7 @@ sub processCategoryChange { $vars->{'type'} = $type; # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("admin/flag-type/edit.html.tmpl", $vars) @@ -246,7 +247,7 @@ sub insert { $vars->{'message'} = "flag_type_created"; # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("global/message.html.tmpl", $vars) @@ -328,7 +329,7 @@ sub update { $vars->{'message'} = "flag_type_changes_saved"; # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("global/message.html.tmpl", $vars) @@ -348,7 +349,7 @@ sub confirmDelete $vars->{'flag_count'} = scalar($count); # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("admin/flag-type/confirm-delete.html.tmpl", $vars) @@ -380,7 +381,7 @@ sub delete { $vars->{'message'} = "flag_type_deleted"; # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("global/message.html.tmpl", $vars) @@ -400,7 +401,7 @@ sub deactivate { $vars->{'flag_type'} = Bugzilla::FlagType::get($::FORM{'id'}); # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("global/message.html.tmpl", $vars) diff --git a/editgroups.cgi b/editgroups.cgi index ca653b77a..a283d0501 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -33,7 +33,7 @@ require "CGI.pl"; ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); if (!UserInGroup("creategroups")) { PutHeader("Not Authorized","Edit Groups","","Not Authorized for this function!"); diff --git a/editkeywords.cgi b/editkeywords.cgi index 4d11a4aae..8ef11aee0 100755 --- a/editkeywords.cgi +++ b/editkeywords.cgi @@ -110,7 +110,7 @@ sub Validate ($$) { ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); unless (UserInGroup("editkeywords")) { PutHeader("Not allowed"); diff --git a/editmilestones.cgi b/editmilestones.cgi index 1fd600900..504a36e7c 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -148,7 +148,7 @@ sub PutTrailer (@) ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); unless (UserInGroup("editcomponents")) { PutHeader("Not allowed"); diff --git a/editparams.cgi b/editparams.cgi index 89099823f..dd61e9543 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -32,7 +32,7 @@ require "CGI.pl"; ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); if (!UserInGroup("tweakparams")) { print "

Sorry, you aren't a member of the 'tweakparams' group.

\n"; diff --git a/editproducts.cgi b/editproducts.cgi index 147fbbc38..423f028fe 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -178,7 +178,7 @@ sub PutTrailer (@) ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); unless (UserInGroup("editcomponents")) { PutHeader("Not allowed"); diff --git a/editusers.cgi b/editusers.cgi index 143e87442..b0e6d621c 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -236,7 +236,7 @@ sub PutTrailer (@) ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); $editall = UserInGroup("editusers"); diff --git a/editversions.cgi b/editversions.cgi index a1bd3e4e4..d47ec5d76 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -157,7 +157,7 @@ sub PutTrailer (@) ConnectToDatabase(); confirm_login(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); unless (UserInGroup("editcomponents")) { PutHeader("Not allowed"); diff --git a/enter_bug.cgi b/enter_bug.cgi index 8f736ff03..6a859264b 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -36,6 +36,7 @@ use strict; use lib qw(.); +use Bugzilla; use Bugzilla::Constants; require "CGI.pl"; @@ -65,6 +66,8 @@ ConnectToDatabase(); # user is right from the start. confirm_login() if AnyEntryGroups(); +my $cgi = Bugzilla->cgi; + if (!defined $::FORM{'product'}) { GetVersionTable(); quietly_check_login(); @@ -88,7 +91,7 @@ if (!defined $::FORM{'product'}) { $vars->{'target'} = "enter_bug.cgi"; $vars->{'format'} = $::FORM{'format'}; - print "Content-type: text/html\n\n"; + print $cgi->header(); $template->process("global/choose-product.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -364,7 +367,7 @@ $vars->{'use_keywords'} = 1 if (@::legal_keywords); my $format = GetFormat("bug/create/create", $::FORM{'format'}, $::FORM{'ctype'}); -print "Content-type: $format->{'ctype'}\n\n"; +print $cgi->header($format->{'ctype'}); $template->process($format->{'template'}, $vars) || ThrowTemplateError($template->error()); diff --git a/globals.pl b/globals.pl index 88c8720e8..1c1ee075a 100644 --- a/globals.pl +++ b/globals.pl @@ -40,7 +40,6 @@ use Bugzilla::Config qw(:DEFAULT ChmodDataFile); sub globals_pl_sillyness { my $zz; $zz = @main::SqlStateStack; - $zz = $main::contenttypes; $zz = @main::default_column_list; $zz = $main::defaultqueryname; $zz = @main::enterable_products; @@ -1536,7 +1535,7 @@ sub GetFormat { { 'template' => $template , 'extension' => $ctype , - 'ctype' => $::contenttypes->{$ctype} , + 'ctype' => Bugzilla::Constants::contenttypes->{$ctype} , }; } diff --git a/importxml.pl b/importxml.pl index 40182c954..afc02faa9 100755 --- a/importxml.pl +++ b/importxml.pl @@ -59,6 +59,8 @@ BEGIN { chdir $::path; use lib ($::path); +use Bugzilla; + use XML::Parser; use Data::Dumper; $Data::Dumper::Useqq = 1; @@ -136,7 +138,7 @@ sub Lock { open(LOCKFID, ">>data/maillock") || die "Can't open data/maillock: $!"; my $val = flock(LOCKFID,2); if (!$val) { # '2' is magic 'exclusive lock' const. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); print "Lock failed: $val\n"; } chmod 0666, "data/maillock"; diff --git a/index.cgi b/index.cgi index 1bdaa5351..f617965d3 100755 --- a/index.cgi +++ b/index.cgi @@ -51,10 +51,12 @@ quietly_check_login('permit_anonymous'); # Main Body Execution ############################################################################### +my $cgi = Bugzilla->cgi; + $vars->{'username'} = $::COOKIE{'Bugzilla_login'} || ''; # Return the appropriate HTTP response headers. -print "Content-Type: text/html\n\n"; +print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("index.html.tmpl", $vars) diff --git a/long_list.cgi b/long_list.cgi index 4c787a34e..08bc6679f 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -24,6 +24,8 @@ use strict; use lib qw(.); +use Bugzilla; + require "CGI.pl"; use vars qw($userid @legal_keywords %FORM); @@ -37,6 +39,8 @@ quietly_check_login(); GetVersionTable(); +my $cgi = Bugzilla->cgi; + my $generic_query = " SELECT bugs.bug_id, @@ -116,8 +120,7 @@ my @time = localtime(time()); my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; my $filename = "bugs-$date.html"; -print "Content-Type: text/html\n"; -print "Content-Disposition: inline; filename=$filename\n\n"; +print $cgi->header(-content_disposition => "inline; filename=$filename"); # Generate and return the UI (HTML page) from the appropriate template. $template->process("bug/show-multiple.html.tmpl", $vars) diff --git a/move.pl b/move.pl index fa34e758d..34c06f720 100755 --- a/move.pl +++ b/move.pl @@ -31,6 +31,7 @@ require "CGI.pl"; use vars qw($template $userid %COOKIE); use Bug; +use Bugzilla; use Bugzilla::BugMail; $::lockcount = 0; @@ -44,6 +45,8 @@ unless ( Param("move-enabled") ) { ConnectToDatabase(); confirm_login(); +my $cgi = Bugzilla->cgi; + sub Log { my ($str) = (@_); Lock(); @@ -59,7 +62,7 @@ sub Lock { open(LOCKFID, ">>data/maillock") || die "Can't open data/maillock: $!"; my $val = flock(LOCKFID,2); if (!$val) { # '2' is magic 'exclusive lock' const. - print "Content-type: text/html\n\n"; + print $cgi->header(); print "Lock failed: $val\n"; } chmod 0666, "data/maillock"; @@ -76,7 +79,7 @@ sub Unlock { } if ( !defined $::FORM{'buglist'} ) { - print "Content-type: text/html\n\n"; + print $cgi->header(); PutHeader("Move Bugs"); print "Move bugs either from the bug display page or perform a "; print "query and change several bugs at once.\n"; @@ -91,7 +94,7 @@ my $movers = Param("movers"); $movers =~ s/\s?,\s?/|/g; $movers =~ s/@/\@/g; unless ($exporter =~ /($movers)/) { - print "Content-type: text/html\n\n"; + print $cgi->header(); PutHeader("Move Bugs"); print "

You do not have permission to move bugs

\n"; PutFooter(); diff --git a/page.cgi b/page.cgi index 48fafb380..2b229e0b6 100755 --- a/page.cgi +++ b/page.cgi @@ -31,6 +31,9 @@ use strict; use lib "."; + +use Bugzilla; + require "CGI.pl"; use vars qw($template $vars); @@ -39,6 +42,8 @@ ConnectToDatabase(); quietly_check_login(); +my $cgi = Bugzilla->cgi; + if ($::FORM{'id'}) { # Remove all dodgy chars, and split into name and ctype. $::FORM{'id'} =~ s/[^\w\-\.]//g; @@ -47,8 +52,8 @@ if ($::FORM{'id'}) { my $format = GetFormat($1, undef, $2); $vars->{'form'} = \%::FORM; - - print "Content-Type: $format->{'ctype'}\n\n"; + + print $cgi->header($format->{'ctype'}); $template->process("pages/$format->{'template'}", $vars) || ThrowTemplateError($template->error()); diff --git a/post_bug.cgi b/post_bug.cgi index 37a43afb4..76d86fe58 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -26,6 +26,7 @@ use strict; use lib qw(.); +use Bugzilla; use Bugzilla::Constants; require "CGI.pl"; @@ -55,6 +56,8 @@ use vars qw($vars $template); ConnectToDatabase(); my $whoid = confirm_login(); +my $cgi = Bugzilla->cgi; + # do a match on the fields if applicable &Bugzilla::User::match_field ({ @@ -85,16 +88,17 @@ if (!$product_id) { # Set cookies my $cookiepath = Param("cookiepath"); if (exists $::FORM{'product'}) { - if (exists $::FORM{'version'}) { - print "Set-Cookie: VERSION-$product=$::FORM{'version'} ; " . - "path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n"; + if (exists $::FORM{'version'}) { + $cgi->send_cookie(-name => "VERSION-$product", + -value => $cgi->param('version'), + -expires => "Fri, 01-Jan-2038 00:00:00 GMT"); } } if (defined $::FORM{'maketemplate'}) { $vars->{'url'} = $::buffer; - print "Content-type: text/html\n\n"; + print $cgi->header(); $template->process("bug/create/make-template.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -491,7 +495,7 @@ if ($::COOKIE{"BUGLIST"}) { } $vars->{'bug_list'} = \@bug_list; -print "Content-type: text/html\n\n"; +print $cgi->header(); $template->process("bug/create/created.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/process_bug.cgi b/process_bug.cgi index 83d601d33..b9414d534 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -31,6 +31,7 @@ my $UserInCanConfirmGroupSet = -1; use lib qw(.); +use Bugzilla; use Bugzilla::Constants; require "CGI.pl"; @@ -58,6 +59,8 @@ use vars qw(%versions ConnectToDatabase(); my $whoid = confirm_login(); +my $cgi = Bugzilla->cgi; + my $requiremilestone = 0; use vars qw($template $vars); @@ -143,7 +146,7 @@ foreach my $field ("dependson", "blocked") { # End Data/Security Validation ###################################################################### -print "Content-type: text/html\n\n"; +print $cgi->header(); $vars->{'title_tag'} = "bug_processed"; # Set the title if we can see a mid-air coming. This test may have false @@ -493,7 +496,7 @@ sub DuplicateUserConfirm { # Confirm whether or not to add the reporter to the cc: list # of the original bug (the one this bug is being duped against). - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("bug/process/confirm-duplicate.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; diff --git a/query.cgi b/query.cgi index 470e3dfbd..b9fb9f794 100755 --- a/query.cgi +++ b/query.cgi @@ -50,6 +50,9 @@ use vars qw( ); ConnectToDatabase(); + +my $cgi = Bugzilla->cgi; + my $userid = 0; if (defined $::FORM{"GoAheadAndLogIn"}) { # We got here from a login page, probably from relogin.cgi. We better @@ -87,8 +90,8 @@ if ($userid) { "($userid, $qname, " . SqlQuote($value) . ")"); } } - print "Set-Cookie: $cookiename= ; path=" . Param("cookiepath") . - "; expires=Sun, 30-Jun-1980 00:00:00 GMT\n"; + $cgi->send_cookie(-name => $cookiename, + -expires => "Fri, 01-Jan-2038 00:00:00 GMT"); } } } @@ -398,6 +401,8 @@ $vars->{'format'} = $::FORM{'format'}; my $format = GetFormat("search/search", $::FORM{'query_format'} || $::FORM{'format'}, $::FORM{'ctype'}); -print "Content-Type: $format->{'ctype'}\n\n"; + +print $cgi->header($format->{'ctype'}); + $template->process($format->{'template'}, $vars) || ThrowTemplateError($template->error()); diff --git a/queryhelp.cgi b/queryhelp.cgi index 175605d9a..a4aff1d07 100755 --- a/queryhelp.cgi +++ b/queryhelp.cgi @@ -35,7 +35,7 @@ quietly_check_login(); GetVersionTable(); -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); my $product = $::FORM{'product'}; diff --git a/quips.cgi b/quips.cgi index 02109fcda..4559e7b91 100755 --- a/quips.cgi +++ b/quips.cgi @@ -39,6 +39,8 @@ require "CGI.pl"; ConnectToDatabase(); confirm_login(); +my $cgi = Bugzilla->cgi; + if (Param('enablequips') eq "off") { ThrowUserError("quips_disabled"); } @@ -129,6 +131,6 @@ if ($action eq "delete") { SendSQL("DELETE FROM quips WHERE quipid = $quipid"); } -print "Content-type: text/html\n\n"; +print $cgi->header(); $template->process("list/quips.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/relogin.cgi b/relogin.cgi index c0182de49..d2ce053a5 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -37,6 +37,8 @@ require "CGI.pl"; ConnectToDatabase(); quietly_check_login(); +my $cgi = Bugzilla->cgi; + if ($::userid) { # Even though we know the userid must match, we still check it in the # SQL as a sanity check, since there is no locking here, and if @@ -49,17 +51,17 @@ if ($::userid) { "AND userid = $::userid"); } -my $cookiepath = Param("cookiepath"); -print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT -Set-Cookie: Bugzilla_logincookie= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT -"; +$cgi->send_cookie(-name => "Bugzilla_login", + -expires => "Tue, 15-Sep-1998 21:49:00 GMT"); +$cgi->send_cookie(-name => "Bugzilla_logincookie", + -expires => "Tue, 15-Sep-1998 21:49:00 GMT"); delete $::COOKIE{"Bugzilla_login"}; -$vars->{'message'} = "logged_out"; +$vars->{'message'} = "logged_out"; $vars->{'user'} = {}; -print "Content-Type: text/html\n\n"; +print $cgi->header(); $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/report.cgi b/report.cgi index d3cde688d..de659ab25 100755 --- a/report.cgi +++ b/report.cgi @@ -26,15 +26,19 @@ use lib "."; require "CGI.pl"; -use vars qw($cgi $template $vars); +use vars qw($template $vars); use Bugzilla; +my $cgi = Bugzilla->cgi; + # Go straight back to query.cgi if we are adding a boolean chart. if (grep(/^cmd-/, $cgi->param())) { my $params = $cgi->canonicalise_query("format", "ctype"); - print "Location: query.cgi?format=" . $cgi->param('query_format') . - ($params ? "&$params" : "") . "\n\n"; + my $location = "query.cgi?format=" . $cgi->param('query_format') . + ($params ? "&$params" : "") . "\n\n"; + + print $cgi->redirect($location); exit; } @@ -52,7 +56,7 @@ my $action = $cgi->param('action') || 'menu'; if ($action eq "menu") { # No need to do any searching in this case, so bail out early. - print "Content-Type: text/html\n\n"; + print $cgi->header(); $template->process("reports/menu.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -276,8 +280,8 @@ $format->{'ctype'} = "text/html" if $::FORM{'debug'}; my @time = localtime(time()); my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; my $filename = "report-$date.$format->{extension}"; -print "Content-Disposition: inline; filename=$filename\n"; -print "Content-Type: $format->{'ctype'}\n\n"; +print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "inline; filename=$filename"); # Problems with this CGI are often due to malformed data. Setting debug=1 # prints out both data structures. diff --git a/reports.cgi b/reports.cgi index d3b1d9431..71ecf6c31 100755 --- a/reports.cgi +++ b/reports.cgi @@ -62,6 +62,8 @@ GetVersionTable(); Bugzilla->switch_to_shadow_db(); +my $cgi = Bugzilla->cgi; + # We only want those products that the user has permissions for. my @myproducts; push( @myproducts, "-All-"); @@ -69,7 +71,7 @@ push( @myproducts, GetSelectableProducts()); if (! defined $FORM{'product'}) { - print "Content-type: text/html\n\n"; + print $cgi->header(); PutHeader("Bug Charts"); choose_product(@myproducts); PutFooter(); @@ -93,10 +95,7 @@ if (! defined $FORM{'product'}) { # This means that is OK to detaint trick_taint($FORM{'product'}); - # Output appropriate HTTP response headers - print "Content-type: text/html\n"; - # Changing attachment to inline to resolve 46897 - zach@zachlipton.com - print "Content-disposition: inline; filename=bugzilla_report.html\n\n"; + print $cgi->header(-Content_Disposition=>'inline; filename=bugzilla_report.html'); PutHeader("Bug Charts"); diff --git a/request.cgi b/request.cgi index 790916359..ae137959d 100755 --- a/request.cgi +++ b/request.cgi @@ -266,7 +266,7 @@ sub queue { $vars->{'types'} = \@types; # Return the appropriate HTTP response headers. - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("request/queue.html.tmpl", $vars) diff --git a/show_activity.cgi b/show_activity.cgi index c748c3df7..8c636ea0b 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -51,7 +51,7 @@ ValidateBugID($::FORM{'id'}); $vars->{'bug_id'} = $::FORM{'id'}; -print "Content-type: text/html\n\n"; +print Bugzilla->cgi->header(); $template->process("bug/activity/show.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/show_bug.cgi b/show_bug.cgi index c4c05f42c..711b7201b 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -24,14 +24,18 @@ use strict; use lib qw(.); +use Bugzilla; + require "CGI.pl"; ConnectToDatabase(); -use vars qw($cgi $template $vars $userid); +use vars qw($template $vars $userid); use Bug; +my $cgi = Bugzilla->cgi; + if ($::FORM{'GoAheadAndLogIn'}) { confirm_login(); } else { @@ -44,7 +48,7 @@ my $single = !$cgi->param('format') # If we don't have an ID, _AND_ we're only doing a single bug, then prompt if (!defined $cgi->param('id') && $single) { - print "Content-type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("bug/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -100,6 +104,7 @@ foreach ($cgi->param("excludefield")) { $vars->{'displayfields'} = \%displayfields; -print "Content-type: $format->{'ctype'}\n\n"; +print $cgi->header($format->{'ctype'}); + $template->process("$format->{'template'}", $vars) || ThrowTemplateError($template->error()); diff --git a/showattachment.cgi b/showattachment.cgi index bfe9ef988..82fc1ba9a 100755 --- a/showattachment.cgi +++ b/showattachment.cgi @@ -25,12 +25,16 @@ use strict; use lib qw(.); -require "CGI.pl"; +use Bugzilla; +use Bugzilla::Util; + +my $cgi = Bugzilla->cgi; + +my $id = $cgi->param('attach_id'); +detaint_natural($id) if defined $id; +$id ||= ""; + +print $cgi->redirect(-location=>"attachment.cgi?id=$id&action=view", + -status=>'301 Permanent Redirect'); -# Redirect to the new interface for displaying attachments. -detaint_natural($::FORM{'attach_id'}) if defined($::FORM{'attach_id'}); -my $id = $::FORM{'attach_id'} || ""; -print "Status: 301 Permanent Redirect\n"; -print "Location: attachment.cgi?id=$id&action=view\n\n"; exit; - diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index 61278b5f3..b035abad4 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -26,6 +26,7 @@ use strict; use lib qw(.); use File::Temp; +use Bugzilla; require "CGI.pl"; @@ -33,6 +34,8 @@ ConnectToDatabase(); quietly_check_login(); +my $cgi = Bugzilla->cgi; + # Connect to the shadow database if this installation is using one to improve # performance. Bugzilla->switch_to_shadow_db(); @@ -228,6 +231,6 @@ $vars->{'rankdir'} = $::FORM{'rankdir'}; $vars->{'showsummary'} = $::FORM{'showsummary'}; # Generate and return the UI (HTML page) from the appropriate template. -print "Content-type: text/html\n\n"; +print $cgi->header(); $template->process("bug/dependency-graph.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/showdependencytree.cgi b/showdependencytree.cgi index 9149296b7..d9f642a3e 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -37,6 +37,8 @@ ConnectToDatabase(); quietly_check_login(); +my $cgi = Bugzilla->cgi; + # Connect to the shadow database if this installation is using one to improve # performance. Bugzilla->switch_to_shadow_db(); @@ -95,7 +97,7 @@ $vars->{'maxdepth'} = $maxdepth; $vars->{'hide_resolved'} = $hide_resolved; $vars->{'canedit'} = UserInGroup("editbugs"); -print "Content-Type: text/html\n\n"; +print $cgi->header(); $template->process("bug/dependency-tree.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/sidebar.cgi b/sidebar.cgi index 7a054abc2..83c89c29c 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -29,6 +29,8 @@ use vars qw( ConnectToDatabase(); quietly_check_login(); +my $cgi = Bugzilla->cgi; + ############################################################################### # Main Body Execution ############################################################################### @@ -63,13 +65,10 @@ if (defined $::COOKIE{'Bugzilla_login'}) { my $useragent = $ENV{HTTP_USER_AGENT}; if ($useragent =~ m:Mozilla/([1-9][0-9]*):i && $1 >= 5 && $useragent !~ m/compatible/i) { - print "Content-type: application/vnd.mozilla.xul+xml\n\n"; + print $cgi->header("application/vnd.mozilla.xul+xml"); # Generate and return the XUL from the appropriate template. $template->process("sidebar.xul.tmpl", $vars) || ThrowTemplateError($template->error()); } else { ThrowUserError("sidebar_supports_mozilla_only"); } - - - diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index 92836f4db..68f046091 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -61,11 +61,6 @@ [% ELSIF error == "bug_error" %] Trying to retrieve bug [% bug.bug_id %] returned the error [% bug.error FILTER html %] - - [% ELSIF error == "cgi_error" %] - [% title = "CGI Error" %] - Bugzilla has had trouble interpreting your CGI request; - [%+ Param('browserbugmessage') %] [% ELSIF error == "chart_data_not_generated" %] The tool which gathers bug counts has not been run yet. diff --git a/token.cgi b/token.cgi index afe6d0361..7f7299a57 100755 --- a/token.cgi +++ b/token.cgi @@ -31,6 +31,8 @@ use lib qw(.); use vars qw($template $vars); +use Bugzilla; + # Include the Bugzilla CGI and general utility library. require "CGI.pl"; @@ -156,7 +158,7 @@ sub requestChangePassword { $vars->{'message'} = "password_change_request"; - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -164,7 +166,7 @@ sub requestChangePassword { sub confirmChangePassword { $vars->{'token'} = $::token; - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("account/password/set-forgotten-password.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -173,7 +175,7 @@ sub cancelChangePassword { $vars->{'message'} = "password_change_canceled"; Token::Cancel($::token, $vars->{'message'}); - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -200,14 +202,14 @@ sub changePassword { $vars->{'message'} = "password_changed"; - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); } sub confirmChangeEmail { # Return HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); $vars->{'token'} = $::token; @@ -249,7 +251,7 @@ sub changeEmail { DeriveGroup($userid); # Return HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); # Let the user know their email address has been changed. @@ -300,7 +302,7 @@ sub cancelChangeEmail { SendSQL("UNLOCK TABLES"); # Return HTTP response headers. - print "Content-Type: text/html\n\n"; + print Bugzilla->cgi->header(); $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/userprefs.cgi b/userprefs.cgi index fa340f50f..206a115a9 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -24,6 +24,8 @@ use strict; use lib qw(.); +use Bugzilla; + require "CGI.pl"; use RelationSet; @@ -354,6 +356,8 @@ confirm_login(); GetVersionTable(); +my $cgi = Bugzilla->cgi; + $vars->{'login'} = $::COOKIE{'Bugzilla_login'}; $vars->{'changes_saved'} = $::FORM{'dosave'}; @@ -390,7 +394,7 @@ SWITCH: for ($current_tab_name) { } # Generate and return the UI (HTML page) from the appropriate template. -print "Content-type: text/html\n\n"; +print $cgi->header(); $template->process("account/prefs/prefs.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/votes.cgi b/votes.cgi index e386d5422..aa2352f4a 100755 --- a/votes.cgi +++ b/votes.cgi @@ -26,14 +26,17 @@ use strict; use lib "."; -require "CGI.pl"; +use Bugzilla; +require "CGI.pl"; # Use global template variables use vars qw($template $vars); ConnectToDatabase(); +my $cgi = Bugzilla->cgi; + # If the action is show_bug, you need a bug_id. # If the action is show_user, you can supply a userid to show the votes for # another user, otherwise you see your own. @@ -86,6 +89,8 @@ exit; # Display the names of all the people voting for this one bug. sub show_bug { + my $cgi = Bugzilla->cgi; + my $bug_id = $::FORM{'bug_id'} || ThrowCodeError("missing_bug_id"); @@ -107,7 +112,7 @@ sub show_bug { $vars->{'users'} = \@users; $vars->{'total'} = $total; - print "Content-type: text/html\n\n"; + print $cgi->header(); $template->process("bug/votes/list-for-bug.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -117,6 +122,8 @@ sub show_bug { sub show_user { GetVersionTable(); + my $cgi = Bugzilla->cgi; + # If a bug_id is given, and we're editing, we'll add it to the votes list. my $bug_id = $::FORM{'bug_id'} || ""; @@ -213,7 +220,7 @@ sub show_user { $vars->{'voting_user'} = { "login" => $name }; $vars->{'products'} = \@products; - print "Content-type: text/html\n\n"; + print $cgi->header(); $template->process("bug/votes/list-for-user.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -224,6 +231,8 @@ sub record_votes { # Begin Data/Security Validation ############################################################################ + my $cgi = Bugzilla->cgi; + # Build a list of bug IDs for which votes have been submitted. Votes # are submitted in form fields in which the field names are the bug # IDs and the field values are the number of votes. @@ -233,13 +242,13 @@ sub record_votes { # that their votes will get nuked if they continue. if (scalar(@buglist) == 0) { if (!defined($::FORM{'delete_all_votes'})) { - print "Content-type: text/html\n\n"; + print $cgi->header(); $template->process("bug/votes/delete-all.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit(); } elsif ($::FORM{'delete_all_votes'} == 0) { - print "Location: votes.cgi\n\n"; + print $cgi->redirect("votes.cgi"); exit(); } } -- cgit v1.2.3-65-gdbad From c831f130e85a02400f16016ec042e8ee1e94cc47 Mon Sep 17 00:00:00 2001 From: "jkeiser%netscape.com" <> Date: Fri, 9 May 2003 09:12:16 +0000 Subject: Make attachment view work again (bug 204964), r=myk@mozilla.org --- attachment.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 26892181f..ece6522d9 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -402,7 +402,7 @@ sub view my $filesize = length($thedata); print Bugzilla->cgi->header(-type=>"$contenttype; name=\"$filename\"", - -content_disposition=> "inline; filename=$filename\n", + -content_disposition=> "inline; filename=$filename", -content_length => $filesize); print $thedata; -- cgit v1.2.3-65-gdbad From 1d057f02b277d29ad4d232d598c49b0344798b40 Mon Sep 17 00:00:00 2001 From: "bbaetz%acm.org" <> Date: Tue, 3 Jun 2003 16:47:37 +0000 Subject: Bug 180635 - Enhance Bugzilla::User to store additional information r=myk,jake --- Bugzilla.pm | 25 +- Bugzilla/BugMail.pm | 33 +- Bugzilla/Error.pm | 3 - Bugzilla/Flag.pm | 40 +- Bugzilla/FlagType.pm | 15 +- Bugzilla/Search.pm | 21 +- Bugzilla/Template.pm | 5 +- Bugzilla/User.pm | 502 ++++++++++++++++++++-- CGI.pl | 47 -- attachment.cgi | 10 +- buglist.cgi | 47 +- docs/xml/administration.xml | 4 +- editusers.cgi | 13 +- globals.pl | 151 +------ post_bug.cgi | 4 +- process_bug.cgi | 10 +- query.cgi | 17 +- relogin.cgi | 4 +- sanitycheck.cgi | 8 +- sidebar.cgi | 20 - template/en/default/attachment/create.html.tmpl | 3 +- template/en/default/bug/show.xml.tmpl | 2 +- template/en/default/flag/list.html.tmpl | 2 +- template/en/default/global/useful-links.html.tmpl | 30 +- template/en/default/list/quips.html.tmpl | 4 +- template/en/default/request/email.txt.tmpl | 4 +- template/en/default/search/knob.html.tmpl | 2 +- template/en/default/sidebar.xul.tmpl | 25 +- token.cgi | 18 +- userprefs.cgi | 9 +- votes.cgi | 6 +- 31 files changed, 647 insertions(+), 437 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla.pm b/Bugzilla.pm index cded650d7..0ce6a5e72 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -77,17 +77,15 @@ sub login { # Compat stuff $::userid = $userid; - &::ConfirmGroup($userid); # Evil compat hack. The cookie stores the id now, not the name, but # old code still looks at this to get the current user's email # so it needs to be set. - $::COOKIE{'Bugzilla_login'} = $_user->{email}; - - $::vars->{'user'} = &::GetUserInfo($userid); + $::COOKIE{'Bugzilla_login'} = $_user->login; } else { # Old compat stuff + undef $_user; $::userid = 0; delete $::COOKIE{'Bugzilla_login'}; delete $::COOKIE{'Bugzilla_logincookie'}; @@ -97,7 +95,12 @@ sub login { # - use Bugzilla->user instead! } - return $userid || 0; + return $_user; +} + +sub logout { + undef $_user; + $::userid = 0; } my $_dbh; @@ -257,8 +260,16 @@ or if the login code has not yet been run. =item C -Logs in a user, returning the userid, or C<0> if there is no logged in user. -See L. +Logs in a user, returning a C object, or C if there is +no logged in user. See L and +L. + +=item C + +Logs out the current user. For the moment, this will just cause calls to +C to return C. Eventually this will handle deleting cookies from +the browser and values from the database, which is currently all handled +by C. =item C diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm index da25b6a70..16b44d6ff 100644 --- a/Bugzilla/BugMail.pm +++ b/Bugzilla/BugMail.pm @@ -111,14 +111,6 @@ sub Send($;$) { # require abuse we do. GetVersionTable(); - # Since any email recipients must be rederived if the user has not - # been rederived since the most recent group change, figure out when that - # is once and determine the need to rederive users using the same DB - # access that gets the user's email address each time a person is - # processed. - SendSQL("SELECT MAX(last_changed) FROM groups"); - ($last_changed) = FetchSQLData(); - # Make sure to clean up _all_ package vars here. Yuck... $nametoexclude = $recipients->{'changer'} || ""; @{$force{'CClist'}} = (exists $recipients->{'cc'} && @@ -710,19 +702,13 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { return; } - - SendSQL("SELECT userid, (refreshed_when > " . SqlQuote($last_changed) . - ") FROM profiles WHERE login_name = " . SqlQuote($person)); - my ($userid, $current) = (FetchSQLData()); + # This routine should really get passed a userid + # This rederives groups as a side effect + my $user = Bugzilla::User->new_from_login($person); + my $userid = $user->id; $seen{$person} = 1; - detaint_natural($userid); - - if (!$current) { - DeriveGroup($userid); - } - # if this person doesn't have permission to see info on this bug, # return. # @@ -732,12 +718,11 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { # quietly disappear from their radar. # return unless CanSeeBug($id, $userid); - # Drop any non-insiders if the comment is private - return if (Param("insidergroup") && + return if (Param("insidergroup") && ($anyprivate != 0) && - (!UserInGroup(Param("insidergroup"), $userid))); + (!$user->groups->{Param("insidergroup")})); # We shouldn't send changedmail if this is a dependency mail, and any of # the depending bugs is not visible to the user. @@ -761,8 +746,8 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { } # Don't send estimated_time if user not in the group, or not enabled if ($f ne 'estimated_time' || - UserInGroup(Param('timetrackinggroup'), $userid)) { - + $user->groups->{Param('timetrackinggroup')}) { + my $desc = $fielddescription{$f}; $head .= FormatDouble($desc, $value); } @@ -781,7 +766,7 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { ($diff->{'fieldname'} eq 'estimated_time' || $diff->{'fieldname'} eq 'remaining_time' || $diff->{'fieldname'} eq 'work_time')) { - if (UserInGroup(Param("timetrackinggroup"), $userid)) { + if ($user->groups->{Param("timetrackinggroup")}) { $add_diff = 1; } } else { diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index 485646274..a23d6db15 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -34,9 +34,6 @@ sub ThrowUserError { $vars->{error} = $error; - # Need to do this until User.pm goes in, so that the footer is correct - $vars->{user} = $::vars->{user}; - Bugzilla->dbh->do("UNLOCK TABLES") if $unlock_tables; print Bugzilla->cgi->header(); diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index f8eb8a4a4..4a1752cd0 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -177,18 +177,14 @@ sub validate { if ($requestee_email ne $flag->{'requestee'}->{'email'}) { # We know the requestee exists because we ran # Bugzilla::User::match_field before getting here. - # ConfirmGroup makes sure their group settings - # are up-to-date or calls DeriveGroups to update them. - my $requestee_id = &::DBname_to_id($requestee_email); - &::ConfirmGroup($requestee_id); + my $requestee = Bugzilla::User->new_from_login($requestee_email); # Throw an error if the user can't see the bug. - if (!&::CanSeeBug($bug_id, $requestee_id)) + if (!&::CanSeeBug($bug_id, $requestee->id)) { ThrowUserError("flag_requestee_unauthorized", { flag_type => $flag->{'type'}, - requestee => - new Bugzilla::User($requestee_id), + requestee => $requestee, bug_id => $bug_id, attach_id => $flag->{target}->{attachment}->{id} }); @@ -198,13 +194,12 @@ sub validate { # the requestee isn't in the group of insiders who can see it. if ($flag->{target}->{attachment}->{exists} && $data->{'isprivate'} - && &::Param("insidergroup") - && !&::UserInGroup(&::Param("insidergroup"), $requestee_id)) + && Param("insidergroup") + && !$requestee->in_group(Param("insidergroup"))) { ThrowUserError("flag_requestee_unauthorized_attachment", { flag_type => $flag->{'type'}, - requestee => - new Bugzilla::User($requestee_id), + requestee => $requestee, bug_id => $bug_id, attach_id => $flag->{target}->{attachment}->{id} }); @@ -236,7 +231,7 @@ sub process { my @old_summaries; foreach my $flag (@$flags) { my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; - $summary .= "($flag->{'requestee'}->{'email'})" if $flag->{'requestee'}; + $summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'}; push(@old_summaries, $summary); } @@ -275,7 +270,7 @@ sub process { my @new_summaries; foreach my $flag (@$flags) { my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; - $summary .= "($flag->{'requestee'}->{'email'})" if $flag->{'requestee'}; + $summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'}; push(@new_summaries, $summary); } @@ -307,7 +302,7 @@ sub create { # Insert a record for the flag into the flags table. my $attach_id = $flag->{'target'}->{'attachment'}->{'id'} || "NULL"; - my $requestee_id = $flag->{'requestee'} ? $flag->{'requestee'}->{'id'} : "NULL"; + my $requestee_id = $flag->{'requestee'} ? $flag->{'requestee'}->id : "NULL"; &::SendSQL("INSERT INTO flags (id, type_id, bug_id, attach_id, requestee_id, setter_id, status, @@ -317,7 +312,7 @@ sub create { $flag->{'target'}->{'bug'}->{'id'}, $attach_id, $requestee_id, - $flag->{'setter'}->{'id'}, + " . $flag->{'setter'}->id . ", '$flag->{'status'}', $timestamp, $timestamp)"); @@ -380,7 +375,7 @@ sub modify { # the flag isn't specifically requestable || $status ne "?" # or the flag isn't being requested || ($flag->{'requestee'} # or the requestee hasn't changed - && ($requestee_email eq $flag->{'requestee'}->{'email'}))); + && ($requestee_email eq $flag->{'requestee'}->login))); # Since the status is validated, we know it's safe, but it's still # tainted, so we have to detaint it before using it in a query. @@ -568,14 +563,15 @@ sub notify { { my @new_cc_list; foreach my $cc (split(/[, ]+/, $flag->{'type'}->{'cc_list'})) { - my $user_id = &::DBname_to_id($cc) || next; - # re-derive permissions if necessary - &::ConfirmGroup($user_id, TABLES_ALREADY_LOCKED); + my $ccuser = Bugzilla::User->new_from_login($cc, + TABLES_ALREADY_LOCKED) + || next; + next if $flag->{'target'}->{'bug'}->{'restricted'} - && !&::CanSeeBug($flag->{'target'}->{'bug'}->{'id'}, $user_id); + && !&::CanSeeBug($flag->{'target'}->{'bug'}->{'id'}, $ccuser->id); next if $flag->{'target'}->{'attachment'}->{'isprivate'} && Param("insidergroup") - && !&::UserInGroup(Param("insidergroup"), $user_id); + && !$ccuser->in_group(Param("insidergroup")); push(@new_cc_list, $cc); } $flag->{'type'}->{'cc_list'} = join(", ", @new_cc_list); @@ -646,7 +642,7 @@ sub perlify_record { id => $id , type => Bugzilla::FlagType::get($type_id) , target => GetTarget($bug_id, $attach_id) , - requestee => new Bugzilla::User($requestee_id) , + requestee => $requestee_id ? new Bugzilla::User($requestee_id) : undef, setter => new Bugzilla::User($setter_id) , status => $status , }; diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index 523f60190..7fbe1f142 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -219,20 +219,17 @@ sub validate { && trim($data->{"requestee_type-$id"})) { my $requestee_email = trim($data->{"requestee_type-$id"}); - my $requestee_id = &::DBname_to_id($requestee_email); # We know the requestee exists because we ran # Bugzilla::User::match_field before getting here. - # ConfirmGroup makes sure their group settings - # are up-to-date or calls DeriveGroups to update them. - &::ConfirmGroup($requestee_id); + my $requestee = Bugzilla::User->new_from_login($requestee_email); # Throw an error if the user can't see the bug. - if (!&::CanSeeBug($bug_id, $requestee_id)) + if (!&::CanSeeBug($bug_id, $requestee->id)) { ThrowUserError("flag_requestee_unauthorized", { flag_type => $flag_type, - requestee => new Bugzilla::User($requestee_id), + requestee => $requestee, bug_id => $bug_id, attach_id => $attach_id }); } @@ -240,13 +237,13 @@ sub validate { # Throw an error if the target is a private attachment and # the requestee isn't in the group of insiders who can see it. if ($attach_id - && &::Param("insidergroup") + && Param("insidergroup") && $data->{'isprivate'} - && !&::UserInGroup(&::Param("insidergroup"), $requestee_id)) + && !$requestee->in_group(Param("insidergroup"))) { ThrowUserError("flag_requestee_unauthorized_attachment", { flag_type => $flag_type, - requestee => new Bugzilla::User($requestee_id), + requestee => $requestee, bug_id => $bug_id, attach_id => $attach_id }); } diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 45c26fdf2..df2ab58e3 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -926,28 +926,31 @@ sub init { # Make sure we create a legal SQL query. @andlist = ("1 = 1") if !@andlist; + my $user = Bugzilla->user; + my $query = "SELECT " . join(', ', @fields) . " FROM $suppstring" . " LEFT JOIN bug_group_map " . " ON bug_group_map.bug_id = bugs.bug_id "; - if (defined @{$::vars->{user}{groupids}} && @{$::vars->{user}{groupids}} > 0) { - $query .= " AND bug_group_map.group_id NOT IN (" . join(',', @{$::vars->{user}{groupids}}) . ") "; - } + if ($user) { + if (%{$user->groups}) { + $query .= " AND bug_group_map.group_id NOT IN (" . join(',', values(%{$user->groups})) . ") "; + } - if ($::vars->{user}{userid}) { - $query .= " LEFT JOIN cc ON cc.bug_id = bugs.bug_id AND cc.who = $::userid "; + $query .= " LEFT JOIN cc ON cc.bug_id = bugs.bug_id AND cc.who = " . $user->id; } $query .= " WHERE " . join(' AND ', (@wherepart, @andlist)) . " AND ((bug_group_map.group_id IS NULL)"; - if ($::vars->{user}{userid}) { - $query .= " OR (bugs.reporter_accessible = 1 AND bugs.reporter = $::userid) " . + if ($user) { + my $userid = $user->id; + $query .= " OR (bugs.reporter_accessible = 1 AND bugs.reporter = $userid) " . " OR (bugs.cclist_accessible = 1 AND cc.who IS NOT NULL) " . - " OR (bugs.assigned_to = $::userid) "; + " OR (bugs.assigned_to = $userid) "; if (Param('useqacontact')) { - $query .= "OR (bugs.qa_contact = $::userid) "; + $query .= "OR (bugs.qa_contact = $userid) "; } } diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index e596af226..7c084ecb9 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -256,7 +256,10 @@ sub create { # Generic linear search function 'lsearch' => \&Bugzilla::Util::lsearch, - # UserInGroup - you probably want to cache this + # Currently logged in user, if any + 'user' => sub { return Bugzilla->user; }, + + # UserInGroup. Deprecated - use the user.* functions instead 'UserInGroup' => \&::UserInGroup, # SendBugMail - sends mail about a bug, using Bugzilla::BugMail.pm diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index fde9d336b..f5df92063 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -19,6 +19,8 @@ # # Contributor(s): Myk Melez # Erik Stambaugh +# Bradley Baetz +# Joel Peshkin ################################################################################ # Module Initialization @@ -30,57 +32,311 @@ use strict; # This module implements utilities for dealing with Bugzilla users. package Bugzilla::User; +use Bugzilla::Config; +use Bugzilla::Util; + ################################################################################ # Functions ################################################################################ -my $user_cache = {}; sub new { - # Returns a hash of information about a particular user. + my $invocant = shift; + return $invocant->_create("userid=?", @_); +} +# This routine is sort of evil. Nothing except the login stuff should +# be dealing with addresses as an input, and they can get the id as a +# side effect of the other sql they have to do anyway. +# Bugzilla::BugMail still does this, probably as a left over from the +# pre-id days. Provide this as a helper, but don't document it, and hope +# that it can go away. +# The request flag stuff also does this, but it really should be passing +# in the id its already had to validate (or the User.pm object, of course) +sub new_from_login { + my $invocant = shift; + return $invocant->_create("login_name=?", @_); +} + +# Internal helper for the above |new| methods +# $cond is a string (including a placeholder ?) for the search +# requirement for the profiles table +sub _create { my $invocant = shift; my $class = ref($invocant) || $invocant; - - my $exists = 1; - my ($id, $name, $email) = @_; - - return undef if !$id; - return $user_cache->{$id} if exists($user_cache->{$id}); - - my $self = { 'id' => $id }; - - bless($self, $class); - - if (!$name && !$email) { - &::PushGlobalSQLState(); - &::SendSQL("SELECT 1, realname, login_name FROM profiles WHERE userid = $id"); - ($exists, $name, $email) = &::FetchSQLData(); - &::PopGlobalSQLState(); + + my $cond = shift; + my $val = shift; + + # We're checking for validity here, so any value is OK + trick_taint($val); + + my $tables_locked_for_derive_groups = shift; + + my $dbh = Bugzilla->dbh; + + my ($id, + $login, + $name, + $mybugslink) = $dbh->selectrow_array(qq{SELECT userid, + login_name, + realname, + mybugslink + FROM profiles + WHERE $cond}, + undef, + $val); + + return undef unless defined $id; + + my $self = { id => $id, + name => $name, + login => $login, + showmybugslink => $mybugslink, + }; + + bless ($self, $class); + + # Now update any old group information if needed + my $result = $dbh->selectrow_array(q{SELECT 1 + FROM profiles, groups + WHERE userid=? + AND profiles.refreshed_when <= + groups.last_changed}, + undef, + $id); + + if ($result) { + $self->derive_groups($tables_locked_for_derive_groups); } - - $self->{'name'} = $name; - $self->{'email'} = $email || "__UNKNOWN__"; - $self->{'exists'} = $exists; - - # Generate a string to identify the user by name + email if the user - # has a name or by email only if she doesn't. - $self->{'identity'} = $name ? "$name <$email>" : $email; - - # Generate a user "nickname" -- i.e. a shorter, not-necessarily-unique name - # by which to identify the user. Currently the part of the user's email - # address before the at sign (@), but that could change, especially if we - # implement usernames not dependent on email address. - my @email_components = split("@", $email); - $self->{'nick'} = $email_components[0]; - - $user_cache->{$id} = $self; - + return $self; } +# Accessors for user attributes +sub id { $_[0]->{id}; } +sub login { $_[0]->{login}; } +sub email { $_[0]->{login}; } +sub name { $_[0]->{name}; } +sub showmybugslink { $_[0]->{showmybugslink}; } + +# Generate a string to identify the user by name + email if the user +# has a name or by email only if she doesn't. +sub identity { + my $self = shift; + + if (!defined $self->{identity}) { + $self->{identity} = + $self->{name} ? "$self->{name} <$self->{login}>" : $self->{login}; + } + + return $self->{identity}; +} + +sub nick { + my $self = shift; + + if (!defined $self->{nick}) { + $self->{nick} = (split(/@/, $self->{login}, 2))[0]; + } + + return $self->{nick}; +} + +sub queries { + my $self = shift; + + return $self->{queries} if defined $self->{queries}; + + my $dbh = Bugzilla->dbh; + my $sth = $dbh->prepare(q{ SELECT name, query, linkinfooter + FROM namedqueries + WHERE userid=? + ORDER BY UPPER(name)}); + $sth->execute($self->{id}); + + my @queries; + while (my $row = $sth->fetch) { + push (@queries, { + name => $row->[0], + query => $row->[1], + linkinfooter => $row->[2], + }); + } + $self->{queries} = \@queries; + + return $self->{queries}; +} + +sub flush_queries_cache { + my $self = shift; + + delete $self->{queries}; +} + +sub groups { + my $self = shift; + + return $self->{groups} if defined $self->{groups}; + + my $dbh = Bugzilla->dbh; + my $groups = $dbh->selectcol_arrayref(q{SELECT DISTINCT groups.name, group_id + FROM groups, user_group_map + WHERE groups.id=user_group_map.group_id + AND user_id=? + AND isbless=0}, + { Columns=>[1,2] }, + $self->{id}); + + # The above gives us an arrayref [name, id, name, id, ...] + # Convert that into a hashref + my %groups = @$groups; + $self->{groups} = \%groups; + + return $self->{groups}; +} + +sub in_group { + my ($self, $group) = @_; + + # If we already have the info, just return it. + return defined($self->{groups}->{$group}) if defined $self->{groups}; + + # Otherwise, go check for it + + my $dbh = Bugzilla->dbh; + + my $res = $dbh->selectrow(q{SELECT 1 + FROM groups, user_group_map + WHERE groups.id=user_group_map.group_id + AND user_group_map.user_id=? + AND isbless=0 + AND groups.name=?}, + undef, + $self->id, + $group); + + return defined($res); +} + +sub derive_groups { + my ($self, $already_locked) = @_; + + my $id = $self->id; + + my $dbh = Bugzilla->dbh; + + my $sth; + + $dbh->do(q{LOCK TABLES profiles WRITE, + user_group_map WRITE, + group_group_map READ, + groups READ}) unless $already_locked; + + # avoid races, we are only up to date as of the BEGINNING of this process + my $time = $dbh->selectrow_array("SELECT NOW()"); + + # first remove any old derived stuff for this user + $dbh->do(q{DELETE FROM user_group_map + WHERE user_id = ? + AND isderived = 1}, + undef, + $id); + + my %groupidsadded = (); + # add derived records for any matching regexps + + $sth = $dbh->prepare("SELECT id, userregexp FROM groups WHERE userregexp != ''"); + $sth->execute; + + my $group_insert; + while (my $row = $sth->fetch) { + if ($self->{login} =~ m/$row->[1]/i) { + $group_insert ||= $dbh->prepare(q{INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES (?, ?, 0, 1)}); + $groupidsadded{$row->[0]} = 1; + $group_insert->execute($id, $row->[0]); + } + } + + # Get a list of the groups of which the user is a member. + my %groupidschecked = (); + + my @groupidstocheck = @{$dbh->selectcol_arrayref(q{SELECT group_id + FROM user_group_map + WHERE user_id=?}, + undef, + $id)}; + + # Each group needs to be checked for inherited memberships once. + my $group_sth; + while (@groupidstocheck) { + my $group = shift @groupidstocheck; + if (!defined($groupidschecked{"$group"})) { + $groupidschecked{"$group"} = 1; + $group_sth ||= $dbh->prepare(q{SELECT grantor_id + FROM group_group_map + WHERE member_id=? + AND isbless=0}); + $group_sth->execute($group); + while (my $groupid = $group_sth->fetchrow_array) { + if (!defined($groupidschecked{"$groupid"})) { + push(@groupidstocheck,$groupid); + } + if (!$groupidsadded{$groupid}) { + $groupidsadded{$groupid} = 1; + $group_insert ||= $dbh->prepare(q{INSERT INTO user_group_map + (user_id, group_id, isbless, isderived) + VALUES (?, ?, 0, 1)}); + $group_insert->execute($id, $groupid); + } + } + } + } + + $dbh->do(q{UPDATE profiles + SET refreshed_when = ? + WHERE userid=?}, + undef, + $time, + $id); + $dbh->do("UNLOCK TABLES") unless $already_locked; +} + +sub can_bless { + my $self = shift; + + return $self->{can_bless} if defined $self->{can_bless}; + + my $dbh = Bugzilla->dbh; + # First check if the user can explicitly bless a group + my $res = $dbh->selectrow_arrayref(q{SELECT 1 + FROM user_group_map + WHERE user_id=? + AND isbless=1}, + undef, + $self->{id}); + if (!$res) { + # Now check if user is a member of a group that can bless a group + $res = $dbh->selectrow_arrayref(q{SELECT 1 + FROM user_group_map, group_group_map + WHERE user_group_map.user_id=? + AND user_group_map.group_id=member_id + AND group_group_map.isbless=1}, + undef, + $self->{id}); + } + + $self->{can_bless} = $res ? 1 : 0; + + return $self->{can_bless}; +} + sub match { # Generates a list of users whose login name (email address) or real name # matches a substring or wildcard. + # This is also called if matches are disabled (for error checking), but + # in this case only the exact match code will end up running. # $str contains the string to match, while $limit contains the # maximum number of records to retrieve. @@ -99,7 +355,8 @@ sub match { my $wildstr = $str; - if ($wildstr =~ s/\*/\%/g) { # don't do wildcards if no '*' in the string + if ($wildstr =~ s/\*/\%/g && # don't do wildcards if no '*' in the string + Param('usermatchmode') ne 'off') { # or if we only want exact matches # Build the query. my $sqlstr = &::SqlQuote($wildstr); @@ -159,7 +416,7 @@ sub match { # order @users by alpha - @users = sort { uc($a->{'email'}) cmp uc($b->{'email'}) } @users; + @users = sort { uc($a->login) cmp uc($b->login) } @users; return \@users; } @@ -251,9 +508,6 @@ sub match_field { } $fields = $expanded_fields; - # Skip all of this if the option has been turned off - return 1 if (&::Param('usermatchmode') eq 'off'); - for my $field (keys %{$fields}) { # Tolerate fields that do not exist. @@ -312,14 +566,14 @@ sub match_field { # skip confirmation for exact matches if ((scalar(@{$users}) == 1) - && (@{$users}[0]->{'email'} eq $query)) + && (@{$users}[0]->{'login'} eq $query)) { # delimit with spaces if necessary if ($vars->{'form'}->{$field}) { $vars->{'form'}->{$field} .= " "; } - $vars->{'form'}->{$field} .= @{$users}[0]->{'email'}; - push @{$vars->{'mform'}->{$field}}, @{$users}[0]->{'email'}; + $vars->{'form'}->{$field} .= @{$users}[0]->{'login'}; + push @{$vars->{'mform'}->{$field}}, @{$users}[0]->{'login'}; next; } @@ -333,8 +587,8 @@ sub match_field { if ($vars->{'form'}->{$field}) { $vars->{'form'}->{$field} .= " "; } - $vars->{'form'}->{$field} .= @{$users}[0]->{'email'}; - push @{$vars->{'mform'}->{$field}}, @{$users}[0]->{'email'}; + $vars->{'form'}->{$field} .= @{$users}[0]->{'login'}; + push @{$vars->{'mform'}->{$field}}, @{$users}[0]->{'login'}; $need_confirm = 1 if &::Param('confirmuniqueusermatch'); } @@ -443,3 +697,159 @@ sub email_prefs { } 1; + +__END__ + +=head1 NAME + +Bugzilla::User - Object for a Bugzilla user + +=head1 SYNOPSIS + + use Bugzilla::User; + + my $user = new Bugzilla::User($id); + +=head1 DESCRIPTION + +This package handles Bugzilla users. Data obtained from here is read-only; +there is currently no way to modify a user from this package. + +Note that the currently logged in user (if any) is available via +Luser|Bugzilla/"user">. + +=head1 METHODS + +=over 4 + +=item C + +Creates a new C object for the given user id. Returns +C if no matching user is found. + +=begin undocumented + +=item C + +Creates a new C object given the provided login. Returns +C if no matching user is found. + +This routine should not be required in general; most scripts should be using +userids instead. + +This routine and C both take an extra optional argument, which is +passed as the argument to C to avoid locking. See that +routine's documentation for details. + +=end undocumented + +=item C + +Returns the userid for this user. + +=item C + +Returns the login name for this user. + +=item C + +Returns the user's email address. Currently this is the same value as the +login. + +=item C + +Returns the 'real' name for this user, if any. + +=item C + +Returns C<1> if the user has set his preference to show the 'My Bugs' link in +the page footer, and C<0> otherwise. + +=item C + +Retruns a string for the identity of the user. This will be of the form +CemailE> if the user has specified a name, and C +otherwise. + +=item C + +Returns a user "nickname" -- i.e. a shorter, not-necessarily-unique name by +which to identify the user. Currently the part of the user's email address +before the at sign (@), but that could change, especially if we implement +usernames not dependent on email address. + +=item C + +Returns an array of the user's named queries, sorted in a case-insensitive +order by name. Each entry is a hash with three keys: + +=over + +=item * + +name - The name of the query + +=item * + +query - The text for the query + +=item * + +linkinfooter - Whether or not the query should be displayed in the footer. + +=back + +=item C + +Some code modifies the set of stored queries. Because C does +not handle these modifications, but does cache the result of calling C +internally, such code must call this method to flush the cached result. + +=item C + +Returns a hashref of group names for groups the user is a member of. The keys +are the names of the groups, whilst the values are the respective group ids. +(This is so that a set of all groupids for groups the user is in can be +obtained by Cgroups})>.) + +=item C + +Determines whether or not a user is in the given group. This method is mainly +intended for cases where we are not looking at the currently logged in user, +and only need to make a quick check for the group, where calling C +and getting all of the groups would be overkill. + +=item C + +Bugzilla allows for group inheritance. When data about the user (or any of the +groups) changes, the database must be updated. Handling updated groups is taken +care of by the constructor. However, when updating the email address, the +user may be placed into different groups, based on a new email regexp. This +method should be called in such a case to force reresolution of these groups. + +=begin undocumented + +This routine takes an optional argument. If true, then this routine will not +lock the tables, but will rely on the caller to ahve done so itsself. + +This is required because mysql will only execute a query if all of the tables +are locked, or if none of them are, not a mixture. If the caller has already +done some locking, then this routine would fail. Thus the caller needs to lock +all the tables required by this method, and then C won't do +any locking. + +This is a really ugly solution, and when Bugzilla supports transactions +instead of using the explicit table locking we were forced to do when thats +all MySQL supported, this will go away. + +=end undocumented + +=item C + +Returns C<1> if the user can bless at least one group. Otherwise returns C<0>. + +=back + +=head1 SEE ALSO + +L diff --git a/CGI.pl b/CGI.pl index 1a6d7c93a..8b33ce102 100644 --- a/CGI.pl +++ b/CGI.pl @@ -202,53 +202,6 @@ sub quietly_check_login { return Bugzilla->login($_[0] ? LOGIN_OPTIONAL : LOGIN_NORMAL); } -# Populate a hash with information about this user. -sub GetUserInfo { - my ($userid) = (@_); - my %user; - my @queries; - my %groups; - my @groupids; - - # No info if not logged in - return \%user if ($userid == 0); - - $user{'login'} = $::COOKIE{"Bugzilla_login"}; - $user{'userid'} = $userid; - - SendSQL("SELECT mybugslink, realname " . - "FROM profiles WHERE userid = $userid"); - ($user{'showmybugslink'}, $user{'realname'}) = FetchSQLData(); - - SendSQL("SELECT name, query, linkinfooter FROM namedqueries " . - "WHERE userid = $userid"); - while (MoreSQLData()) { - my %query; - ($query{'name'}, $query{'query'}, $query{'linkinfooter'}) = - FetchSQLData(); - push(@queries, \%query); - } - - $user{'queries'} = \@queries; - - $user{'canblessany'} = UserCanBlessAnything(); - - SendSQL("SELECT DISTINCT id, name FROM groups, user_group_map " . - "WHERE groups.id = user_group_map.group_id " . - "AND user_id = $userid " . - "AND NOT isbless"); - while (MoreSQLData()) { - my ($id, $name) = FetchSQLData(); - push(@groupids,$id); - $groups{$name} = 1; - } - - $user{'groups'} = \%groups; - $user{'groupids'} = \@groupids; - - return \%user; -} - sub CheckEmailSyntax { my ($addr) = (@_); my $match = Param('emailregexp'); diff --git a/attachment.cgi b/attachment.cgi index ece6522d9..e70fb88f4 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -693,11 +693,11 @@ sub update "flaginclusions AS i READ, flagexclusions AS e READ, " . # cc, bug_group_map, user_group_map, and groups are in here so we # can check the permissions of flag requestees and email addresses - # on the flag type cc: lists via the ConfirmGroup and CanSeeBug - # function calls in Flag::notify. group_group_map is in here in case - # ConfirmGroup needs to call DeriveGroup. profiles and user_group_map - # would be READ locks instead of WRITE locks if it weren't for - # DeriveGroup, which needs to write to those tables. + # on the flag type cc: lists via the CanSeeBug + # function call in Flag::notify. group_group_map is in here in case + # Bugzilla::User needs to rederive groups. profiles and + # user_group_map would be READ locks instead of WRITE locks if it + # weren't for derive_groups, which needs to write to those tables. "bugs READ, profiles WRITE, " . "cc READ, bug_group_map READ, user_group_map WRITE, " . "group_group_map READ, groups READ"); diff --git a/buglist.cgi b/buglist.cgi index 088f69546..0f7dda0ac 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -272,15 +272,9 @@ if ($::FORM{'cmdtype'} eq "dorem") { my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); my $qname = SqlQuote($::FORM{'namedcmd'}); SendSQL("DELETE FROM namedqueries WHERE userid = $userid AND name = $qname"); - # Now remove this query from the footer - my $count = 0; - foreach my $q (@{$::vars->{'user'}{'queries'}}) { - if ($q->{'name'} eq $::FORM{'namedcmd'}) { - splice(@{$::vars->{'user'}{'queries'}}, $count, 1); - last; - } - $count++; - } + + # Now reset the cached queries + Bugzilla->user->flush_queries_cache(); print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. @@ -317,6 +311,14 @@ elsif ($::FORM{'cmdtype'} eq "doit" && $::FORM{'remember'}) { my $tofooter = $::FORM{'tofooter'} ? 1 : 0; + $vars->{'message'} = "buglist_new_named_query"; + + # We want to display the correct message. Check if it existed before + # we insert, because ->queries may fetch from the db anyway + if (grep { $_->{name} eq $name } @{Bugzilla->user->queries()}) { + $vars->{'message'} = "buglist_updated_named_query"; + } + SendSQL("SELECT query FROM namedqueries WHERE userid = $userid AND name = $qname"); if (FetchOneColumn()) { SendSQL("UPDATE namedqueries @@ -327,28 +329,11 @@ elsif ($::FORM{'cmdtype'} eq "doit" && $::FORM{'remember'}) { SendSQL("REPLACE INTO namedqueries (userid, name, query, linkinfooter) VALUES ($userid, $qname, $qbuffer, $tofooter)"); } - - my $new_in_footer = $tofooter; - $vars->{'message'} = "buglist_new_named_query"; - - # Don't add it to the list if they are reusing an existing query name. - foreach my $query (@{$vars->{'user'}{'queries'}}) { - if ($query->{'name'} eq $name) { - $vars->{'message'} = "buglist_updated_named_query"; - if ($query->{'linkinfooter'} == 1) { - $new_in_footer = 0; - } - last; - } - } - - if ($new_in_footer) { - my %query = (name => $name, - query => $::buffer, - linkinfooter => $tofooter); - push(@{$vars->{'user'}{'queries'}}, \%query); - } - + + # Make sure to invalidate any cached query data, so that the footer is + # correctly displayed + Bugzilla->user->flush_queries_cache(); + $vars->{'queryname'} = $name; } } diff --git a/docs/xml/administration.xml b/docs/xml/administration.xml index 2382fca82..ecf465fdc 100644 --- a/docs/xml/administration.xml +++ b/docs/xml/administration.xml @@ -1385,7 +1385,7 @@ skip-networking positive check, which returns 1 (allow) if certain conditions are true, or a negative check, which returns 0 (deny.) E.g.: if ($field eq "qacontact") { - if (UserInGroup("quality_assurance")) { + if (Bugzilla->user->groups("quality_assurance")) { return 1; } else { @@ -1395,7 +1395,7 @@ skip-networking This says that only users in the group "quality_assurance" can change the QA Contact field of a bug. Getting more weird: if (($field eq "priority") && - ($vars->{'user'}{'login'} =~ /.*\@example\.com$/)) + (Bugzilla->user->email =~ /.*\@example\.com$/)) { if ($oldvalue eq "P1") { return 1; diff --git a/editusers.cgi b/editusers.cgi index b0e6d621c..b4ed7c2d0 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -34,6 +34,8 @@ use lib "."; require "CGI.pl"; require "globals.pl"; +use Bugzilla::User; + # Shut up misguided -w warnings about "used only once". "use vars" just # doesn't work for me. @@ -241,7 +243,7 @@ print Bugzilla->cgi->header(); $editall = UserInGroup("editusers"); if (!$editall) { - if (!UserCanBlessAnything()) { + if (!Bugzilla->user->can_bless) { PutHeader("Not allowed"); print "Sorry, you aren't a member of the 'editusers' group, and you\n"; print "don't have permissions to put people in or out of any group.\n"; @@ -483,7 +485,7 @@ if ($action eq 'new') { print "OK, done.
\n"; SendSQL("SELECT last_insert_id()"); my ($newuserid) = FetchSQLData(); - DeriveGroup($newuserid); + print "To change ${user}'s permissions, go back and edit this user"; print "

\n"; PutTrailer($localtrailer, @@ -682,7 +684,9 @@ if ($action eq 'edit') { my ($thisuserid, $realname, $disabledtext) = FetchSQLData(); if ($thisuserid > 0) { - DeriveGroup($thisuserid); + # Force groups to be up to date + my $changeduser = new Bugzilla::User($thisuserid); + $changeduser->derive_groups(); } print "

\n"; print "\n"; @@ -844,7 +848,8 @@ if ($action eq 'update') { print "Updated user's name.
\n"; } - DeriveGroup($thisuserid); + my $changeduser = new Bugzilla::User($thisuserid); + $changeduser->derive_groups(); PutTrailer($localtrailer); exit; diff --git a/globals.pl b/globals.pl index 1c1ee075a..9c36e9003 100644 --- a/globals.pl +++ b/globals.pl @@ -510,10 +510,9 @@ sub CanEditProductId { my $query = "SELECT group_id FROM group_control_map " . "WHERE product_id = $productid " . "AND canedit != 0 "; - if ((defined @{$::vars->{user}{groupids}}) - && (@{$::vars->{user}{groupids}} > 0)) { + if (defined Bugzilla->user && %{Bugzilla->user->groups}) { $query .= "AND group_id NOT IN(" . - join(',',@{$::vars->{user}{groupids}}) . ") "; + join(',', values(%{Bugzilla->user->groups})) . ") "; } $query .= "LIMIT 1"; PushGlobalSQLState(); @@ -533,10 +532,9 @@ sub CanEnterProduct { "LEFT JOIN group_control_map " . "ON group_control_map.product_id = products.id " . "AND group_control_map.entry != 0 "; - if ((defined @{$::vars->{user}{groupids}}) - && (@{$::vars->{user}{groupids}} > 0)) { + if (defined Bugzilla->user && %{Bugzilla->user->groups}) { $query .= "AND group_id NOT IN(" . - join(',',@{$::vars->{user}{groupids}}) . ") "; + join(',', values(%{Bugzilla->user->groups})) . ") "; } $query .= "WHERE products.name = " . SqlQuote($productname) . " LIMIT 1"; PushGlobalSQLState(); @@ -566,10 +564,9 @@ sub GetSelectableProducts { $query .= "AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY . " "; } - if ((defined @{$::vars->{user}{groupids}}) - && (@{$::vars->{user}{groupids}} > 0)) { + if (defined Bugzilla->user && %{Bugzilla->user->groups}) { $query .= "AND group_id NOT IN(" . - join(',',@{$::vars->{user}{groupids}}) . ") "; + join(',', values(%{Bugzilla->user->groups})) . ") "; } $query .= "WHERE group_id IS NULL ORDER BY name"; PushGlobalSQLState(); @@ -722,99 +719,6 @@ sub Crypt { return $cryptedpassword; } -# ConfirmGroup(userid) is called prior to any activity that relies -# on user_group_map to ensure that derived group permissions are up-to-date. -# Permissions must be rederived if ANY groups have a last_changed newer -# than the profiles.refreshed_when value. -sub ConfirmGroup { - my ($user, $locked) = (@_); - PushGlobalSQLState(); - SendSQL("SELECT userid FROM profiles, groups WHERE userid = $user " . - "AND profiles.refreshed_when <= groups.last_changed "); - my $ret = FetchSQLData(); - PopGlobalSQLState(); - if ($ret) { - DeriveGroup($user, $locked); - } -} - -# DeriveGroup removes and rederives all derived group permissions for -# the specified user. If $locked is true, Bugzilla has already locked -# the necessary tables as part of a larger transaction, so this function -# shouldn't lock them again (since then tables not part of this function's -# lock will get unlocked). -sub DeriveGroup { - my ($user, $locked) = (@_); - PushGlobalSQLState(); - - SendSQL("LOCK TABLES profiles WRITE, user_group_map WRITE, group_group_map READ, groups READ") - unless $locked; - - # avoid races, we are only as up to date as the BEGINNING of this process - SendSQL("SELECT login_name, NOW() FROM profiles WHERE userid = $user"); - my ($login, $starttime) = FetchSQLData(); - - # first remove any old derived stuff for this user - SendSQL("DELETE FROM user_group_map WHERE user_id = $user " . - "AND isderived = 1"); - - my %groupidsadded = (); - # add derived records for any matching regexps - SendSQL("SELECT id, userregexp FROM groups WHERE userregexp != ''"); - while (MoreSQLData()) { - my ($groupid, $rexp) = FetchSQLData(); - if ($login =~ m/$rexp/i) { - PushGlobalSQLState(); - $groupidsadded{$groupid} = 1; - SendSQL("INSERT INTO user_group_map " . - "(user_id, group_id, isbless, isderived) " . - "VALUES ($user, $groupid, 0, 1)"); - PopGlobalSQLState(); - - } - } - - # Get a list of the groups of which the user is a member. - my %groupidschecked = (); - my @groupidstocheck = (); - SendSQL("SELECT group_id FROM user_group_map WHERE user_id = $user - AND NOT isbless"); - while (MoreSQLData()) { - my ($groupid) = FetchSQLData(); - push(@groupidstocheck,$groupid); - } - - # Each group needs to be checked for inherited memberships once. - while (@groupidstocheck) { - my $group = shift @groupidstocheck; - if (!defined($groupidschecked{"$group"})) { - $groupidschecked{"$group"} = 1; - SendSQL("SELECT grantor_id FROM group_group_map WHERE" - . " member_id = $group AND NOT isbless"); - while (MoreSQLData()) { - my ($groupid) = FetchSQLData(); - if (!defined($groupidschecked{"$groupid"})) { - push(@groupidstocheck,$groupid); - } - if (!$groupidsadded{$groupid}) { - $groupidsadded{$groupid} = 1; - PushGlobalSQLState(); - SendSQL("INSERT INTO user_group_map" - . " (user_id, group_id, isbless, isderived)" - . " VALUES ($user, $groupid, 0, 1)"); - PopGlobalSQLState(); - } - } - } - } - - SendSQL("UPDATE profiles SET refreshed_when = " . - SqlQuote($starttime) . "WHERE userid = $user"); - SendSQL("UNLOCK TABLES"); - PopGlobalSQLState(); -}; - - sub DBID_to_real_or_loginname { my ($id) = (@_); PushGlobalSQLState(); @@ -1189,23 +1093,8 @@ sub SplitEnumType { return @result; } -# UserInGroup returns information aboout the current user if no second -# parameter is specified sub UserInGroup { - my ($groupname, $userid) = (@_); - if (!$userid) { - return $::vars->{'user'}{'groups'}{$_[0]}; - } - PushGlobalSQLState(); - $userid ||= $::userid; - SendSQL("SELECT groups.id FROM groups, user_group_map - WHERE groups.id = user_group_map.group_id - AND user_group_map.user_id = $userid - AND isbless = 0 - AND groups.name = " . SqlQuote($groupname)); - my $result = FetchOneColumn(); - PopGlobalSQLState(); - return defined($result); + return defined Bugzilla->user && defined Bugzilla->user->groups->{$_[0]}; } sub UserCanBlessGroup { @@ -1238,32 +1127,6 @@ sub UserCanBlessGroup { return $result; } -sub UserCanBlessAnything { - PushGlobalSQLState(); - # check if user explicitly can bless a group - SendSQL("SELECT group_id FROM user_group_map - WHERE user_id = $::userid AND isbless = 1"); - my $result = FetchOneColumn(); - PopGlobalSQLState(); - if ($result) { - return 1; - } - PushGlobalSQLState(); - # check if user is a member of a group that can bless this group - SendSQL("SELECT groups.id FROM groups, user_group_map, - group_group_map - WHERE groups.id = grantor_id - AND user_group_map.user_id = $::userid - AND group_group_map.isbless = 1 - AND user_group_map.group_id = member_id"); - $result = FetchOneColumn(); - PopGlobalSQLState(); - if ($result) { - return 1; - } - return 0; -} - sub BugInGroup { my ($bugid, $groupname) = (@_); PushGlobalSQLState(); diff --git a/post_bug.cgi b/post_bug.cgi index 76d86fe58..f0d4f0816 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -54,7 +54,7 @@ sub sillyness { use vars qw($vars $template); ConnectToDatabase(); -my $whoid = confirm_login(); +my $user = confirm_login(); my $cgi = Bugzilla->cgi; @@ -454,7 +454,7 @@ if (UserInGroup("editbugs")) { "($id, $i)"); push(@all_deps, $i); # list for mailing dependent bugs # Log the activity for the other bug: - LogActivityEntry($i, $me, "", $id, $whoid, $timestamp); + LogActivityEntry($i, $me, "", $id, $user->id, $timestamp); } my $tmp = $me; $me = $target; diff --git a/process_bug.cgi b/process_bug.cgi index b9414d534..45974d465 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -57,7 +57,8 @@ use vars qw(%versions ); ConnectToDatabase(); -my $whoid = confirm_login(); +my $user = confirm_login(); +my $whoid = $user->id; my $cgi = Bugzilla->cgi; @@ -1093,9 +1094,10 @@ foreach my $id (@idlist) { "keywords $write, longdescs $write, fielddefs $write, " . "bug_group_map $write, flags $write, duplicates $write," . # user_group_map would be a READ lock except that Flag::process - # may call Flag::notify, which calls ConfirmGroup, which might - # call DeriveGroup, which wants a WRITE lock on that table. - # group_group_map is in here at all because DeriveGroups needs it. + # may call Flag::notify, which creates a new user object, + # which might call derive_groups, which wants a WRITE lock on that + # table. group_group_map is in here at all because derive_groups + # needs it. "user_group_map $write, group_group_map READ, flagtypes READ, " . "flaginclusions AS i READ, flagexclusions AS e READ, " . "keyworddefs READ, groups READ, attachments READ, " . diff --git a/query.cgi b/query.cgi index b9fb9f794..e450898da 100755 --- a/query.cgi +++ b/query.cgi @@ -53,19 +53,21 @@ ConnectToDatabase(); my $cgi = Bugzilla->cgi; -my $userid = 0; if (defined $::FORM{"GoAheadAndLogIn"}) { # We got here from a login page, probably from relogin.cgi. We better # make sure the password is legit. - $userid = confirm_login(); + confirm_login(); } else { - $userid = quietly_check_login(); + quietly_check_login(); } +my $user = Bugzilla->user; +my $userid = $user ? $user->id : 0; + # Backwards compatibility hack -- if there are any of the old QUERY_* # cookies around, and we are logged in, then move them into the database # and nuke the cookie. This is required for Bugzilla 2.8 and earlier. -if ($userid) { +if ($user) { my @oldquerycookies; foreach my $i (keys %::COOKIE) { if ($i =~ /^QUERY_(.*)$/) { @@ -97,7 +99,7 @@ if ($userid) { } if ($::FORM{'nukedefaultquery'}) { - if ($userid) { + if ($user) { SendSQL("DELETE FROM namedqueries " . "WHERE userid = $userid AND name = '$::defaultqueryname'"); } @@ -105,7 +107,7 @@ if ($::FORM{'nukedefaultquery'}) { } my $userdefaultquery; -if ($userid) { +if ($user) { SendSQL("SELECT query FROM namedqueries " . "WHERE userid = $userid AND name = '$::defaultqueryname'"); $userdefaultquery = FetchOneColumn(); @@ -308,7 +310,6 @@ $vars->{'rep_platform'} = \@::legal_platform; $vars->{'op_sys'} = \@::legal_opsys; $vars->{'priority'} = \@::legal_priority; $vars->{'bug_severity'} = \@::legal_severity; -$vars->{'userid'} = $userid; # Boolean charts my @fields; @@ -362,7 +363,7 @@ for (my $chart = 0; $::FORM{"field$chart-0-0"}; $chart++) { $default{'charts'} = \@charts; # Named queries -if ($userid) { +if ($user) { my @namedqueries; SendSQL("SELECT name FROM namedqueries " . "WHERE userid = $userid AND name != '$::defaultqueryname' " . diff --git a/relogin.cgi b/relogin.cgi index d2ce053a5..65cb07b25 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -59,7 +59,9 @@ $cgi->send_cookie(-name => "Bugzilla_logincookie", delete $::COOKIE{"Bugzilla_login"}; $vars->{'message'} = "logged_out"; -$vars->{'user'} = {}; + +# This entire script should eventually just become a call to Bugzilla->logout +Bugzilla->logout; print $cgi->header(); $template->process("global/message.html.tmpl", $vars) diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 98734e5b0..aab9c8d38 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -106,12 +106,16 @@ if (exists $::FORM{'rederivegroups'}) { } # rederivegroupsnow is REALLY only for testing. +# If it wasn't, then we'd do this the faster way as a per-group +# thing rather than per-user for group inheritance if (exists $::FORM{'rederivegroupsnow'}) { + require Bugzilla::User; Status("OK, now rederiving groups."); SendSQL("SELECT userid FROM profiles"); while ((my $id) = FetchSQLData()) { - DeriveGroup($id); - Status("Group $id"); + my $user = new Bugzilla::User($id); + $user->derive_groups(); + Status("User $id"); } } diff --git a/sidebar.cgi b/sidebar.cgi index 83c89c29c..cf801eba3 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -35,26 +35,6 @@ my $cgi = Bugzilla->cgi; # Main Body Execution ############################################################################### -$vars->{'username'} = $::COOKIE{'Bugzilla_login'} || ''; - -if (defined $::COOKIE{'Bugzilla_login'}) { - SendSQL("SELECT mybugslink, userid FROM profiles " . - "WHERE login_name = " . SqlQuote($::COOKIE{'Bugzilla_login'})); - my ($mybugslink, $userid) = (FetchSQLData()); - $vars->{'userid'} = $userid; - $vars->{'canblessanything'} = UserCanBlessAnything(); - if ($mybugslink) { - my $mybugstemplate = Param("mybugstemplate"); - my %substs = ( 'userid' => url_quote($::COOKIE{'Bugzilla_login'}) ); - $vars->{'mybugsurl'} = PerformSubsts($mybugstemplate, \%substs); - } - SendSQL("SELECT name FROM namedqueries WHERE userid = $userid AND linkinfooter"); - while (MoreSQLData()) { - my ($name) = FetchSQLData(); - push(@{$vars->{'namedqueries'}}, $name); - } -} - # This sidebar is currently for use with Mozilla based web browsers. # Internet Explorer 6 is supposed to have a similar feature, but it # most likely won't support XUL ;) When that does come out, this diff --git a/template/en/default/attachment/create.html.tmpl b/template/en/default/attachment/create.html.tmpl index a298df5a9..56bad7fae 100644 --- a/template/en/default/attachment/create.html.tmpl +++ b/template/en/default/attachment/create.html.tmpl @@ -123,7 +123,8 @@ [% END %] - [% IF (user.userid != bugassignee_id) AND UserInGroup("editbugs") %] + + [% IF (user.id != bugassignee_id) AND user.groups.editbugs %] @@ -72,7 +59,7 @@ [% ', parameters' IF user.groups.tweakparams %] [% ', users' IF user.groups.editusers - || user.canblessany %] + || user.can_bless %] [% ', products' IF user.groups.editcomponents %] [% ', flags' @@ -91,9 +78,14 @@ [%# Preset queries %] [% preset_queries = user.showmybugslink %] - [% FOREACH q = user.queries %] - [% SET preset_queries = 1 IF q.linkinfooter %] - [% END %] + [% IF NOT preset_queries %] + [% FOREACH q = user.queries %] + [% IF q.linkinfooter %] + [% preset_queries = 1 %] + [% LAST %] + [% END %] + [% END %] + [% END %] [% IF preset_queries %] diff --git a/template/en/default/list/quips.html.tmpl b/template/en/default/list/quips.html.tmpl index 4a6ef1ad5..d9f092db2 100644 --- a/template/en/default/list/quips.html.tmpl +++ b/template/en/default/list/quips.html.tmpl @@ -35,7 +35,7 @@

Your quip '[% added_quip FILTER html %]' has been added. - [% IF Param("enablequips") == "approved" AND !UserInGroup('admin') %] + [% IF Param("enablequips") == "approved" AND !user.groups.admin %] It will be used as soon as it gets approved. [% END %] @@ -58,7 +58,7 @@ Bugzilla will pick a random quip for the headline on each bug list, and you can extend the quip list. Type in something clever or funny or boring (but not obscene or offensive, please) and bonk on the button. - [% IF Param("enablequips") == "approved" AND !UserInGroup('admin') %] + [% IF Param("enablequips") == "approved" AND !user.groups.admin %] Note that your quip has to be approved before it is used. [% END %]

diff --git a/template/en/default/request/email.txt.tmpl b/template/en/default/request/email.txt.tmpl index e35132181..cd7e43358 100644 --- a/template/en/default/request/email.txt.tmpl +++ b/template/en/default/request/email.txt.tmpl @@ -43,7 +43,7 @@ Subject: [% flag.type.name %] [%+ subject_status %]: [Bug [% flag.target.bug.id [%+ USE wrap -%] [%- FILTER bullet = wrap(80) -%] -[% user.realname %] <[% user.login %]> has [% statuses.${flag.status} %] [%+ to_identity %] for [% flag.type.name %]: +[% user.identity %] has [% statuses.${flag.status} %] [%+ to_identity %] for [% flag.type.name %]: Bug [% bugidsummary %] [% END %] @@ -58,7 +58,7 @@ Attachment [% attidsummary %] [%- FILTER bullet = wrap(80) %] [% IF form.comment.length > 0 %] -------- Additional Comments from [% user.realname %] <[% user.login %]> +------- Additional Comments from [% user.identity %] [%+ form.comment %] [% END %] diff --git a/template/en/default/search/knob.html.tmpl b/template/en/default/search/knob.html.tmpl index 50e8fc646..2c99c3f82 100644 --- a/template/en/default/search/knob.html.tmpl +++ b/template/en/default/search/knob.html.tmpl @@ -30,7 +30,7 @@ "Last Changed" => "Last Changed" } %]
-[% IF NOT userid %] +[% IF NOT user %] [% ELSE %] + +[% section_num = 0 %] +[% FOREACH section = sections %] + [% section_num = section_num + 1 %] + + [% FOREACH group = section.groups %] + [% IF group.context %] + [% FOREACH line = group.context %] + + [% END %] + [% END %] + [% IF group.plus.size %] + [% IF group.minus.size %] + [% i = 0 %] + [% WHILE (i < group.plus.size || i < group.minus.size) %] + [% currentloop = 0 %] + [% WHILE currentloop < 500 && (i < group.plus.size || i < group.minus.size) %] + + + + + [% currentloop = currentloop + 1 %] + [% i = i + 1 %] + [% END %] + [% END %] + [% ELSE %] + [% FOREACH line = group.plus %] + [% IF file.is_add %] + + + + [% ELSE %] + + + + + [% END %] + [% END %] + [% END %] + [% ELSE %] + [% IF group.minus.size %] + [% FOREACH line = group.minus %] + [% IF file.is_remove %] + + + + [% ELSE %] + + + + + [% END %] + [% END %] + [% END %] + [% END %] + [% END %] +[% END %] + +
Reassignment: diff --git a/template/en/default/bug/show.xml.tmpl b/template/en/default/bug/show.xml.tmpl index 618745902..45ef1712a 100644 --- a/template/en/default/bug/show.xml.tmpl +++ b/template/en/default/bug/show.xml.tmpl @@ -25,7 +25,7 @@ diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl index 9ca1c25c7..50752a0df 100644 --- a/template/en/default/flag/list.html.tmpl +++ b/template/en/default/flag/list.html.tmpl @@ -116,7 +116,7 @@ id="requestee-[% flag.id %]" name="requestee-[% flag.id %]" [% IF flag.status == "?" && flag.requestee %] - value="[% flag.requestee.email FILTER html %]" + value="[% flag.requestee.login FILTER html %]" [% END %] >) diff --git a/template/en/default/global/useful-links.html.tmpl b/template/en/default/global/useful-links.html.tmpl index fac714b0e..e7588db7d 100644 --- a/template/en/default/global/useful-links.html.tmpl +++ b/template/en/default/global/useful-links.html.tmpl @@ -19,19 +19,6 @@ # Contributor(s): Gervase Markham #%] -[%# INTERFACE: - # user: hash. Information about the user. If the user is not logged in, - # user.login is undefined. - # login: string. The user's Bugzilla login email address. - # showmybugslink: boolean. True if user wants My Bugs in the footer. - # queries: list of strings. The names of those of the user's named - # queries which should be displayed in the footer. - # groups: hash. Keys are group names, values are true if user in that group. - # The keys used in this template are - # tweakparams, editcomponents, creategroups, editkeywords, confirm, - # editbugs, editusers. - #%] - [%# Migration note: this whole file corresponds to the old %commandmenu% substitution param in 'footerhtml' %] @@ -51,14 +38,14 @@ Reports - [% IF user.login %] + [% IF user %] [% email = user.login FILTER url_quote %] | My Requests [% ELSE %] | Requests [% END %] - [% IF user.login && Param('usevotes') %] + [% IF user && Param('usevotes') %] | My Votes [% END %]
+ [% IF file.is_add %] + Added + [% ELSIF file.is_remove %] + [% IF bonsai_prefix %] + Removed + [% ELSE %] + Removed + [% END %] + [% ELSE %] + [% IF bonsai_prefix %] + + [% END %] + [% IF section.old_lines > 1 %] + Lines [% section.old_start %]-[% section.old_start + section.old_lines - 1 %] + [% ELSE %] + Line [% section.old_start %] + [% END %] + [% IF bonsai_prefix %] + + [% END %] + [% END %] + (Link Here) +
[% line FILTER html %]
[% line FILTER html %]
[% group.minus.$i FILTER html %]
[% group.plus.$i FILTER html %]
[% line FILTER html %]
[% line FILTER html %]
[% line FILTER html %]
[% line FILTER html %]
diff --git a/template/en/default/attachment/diff-footer.html.tmpl b/template/en/default/attachment/diff-footer.html.tmpl new file mode 100644 index 000000000..4eb94aca2 --- /dev/null +++ b/template/en/default/attachment/diff-footer.html.tmpl @@ -0,0 +1,33 @@ + +[%# 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): John Keiser + #%] + + + +[% IF headers %] + +
+ + [% PROCESS global/footer.html.tmpl %] + +[% ELSE %] + + +[% END %] diff --git a/template/en/default/attachment/diff-header.html.tmpl b/template/en/default/attachment/diff-header.html.tmpl new file mode 100644 index 000000000..c1b70173e --- /dev/null +++ b/template/en/default/attachment/diff-header.html.tmpl @@ -0,0 +1,307 @@ + +[%# 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): John Keiser + #%] + +[%# Define strings that will serve as the title and header of this page %] + +[% title = BLOCK %]Attachment #[% attachid %] for Bug #[% bugid %][% END %] + +[% style = BLOCK %] +.file_head { + font-size: x-large; + font-weight: bold; + background-color: #d3d3d3; + border: 1px solid black; + width: 100%; +} +.file_collapse { + display: none; +} +.section_head { + width: 100%; + font-weight: bold; + background-color: #d3d3d3; + border: 1px solid black; + text-align: left; +} +table.file_table { + table-layout: fixed; + width: 100%; + empty-cells: show; + border-spacing: 0px; + border-collapse: collapse; +} +tbody.file td { + border-left: 1px dashed black; + border-right: 1px dashed black; + width: 50%; +} +tbody.file pre { + display: inline; + white-space: -moz-pre-wrap; + font-size: 0.9em; +} +tbody.file pre:empty { + display: block; + height: 1em; +} +.changed { + background-color: lightblue; +} +.added { + background-color: lightgreen; +} +.removed { + background-color: #FFCC99; +} +.warning { + color: red +} +[% END %] + +[%# SCRIPT FUNCTIONS %] +[% javascript = BLOCK %] + function collapse_all() { + var elem = document.checkboxform.firstChild; + while (elem != null) { + if (elem.firstChild != null) { + var tbody = elem.firstChild.nextSibling; + if (tbody.className == 'file') { + tbody.className = 'file_collapse'; + twisty = get_twisty_from_tbody(tbody); + twisty.firstChild.nodeValue = '(+)'; + twisty.nextSibling.checked = false; + } + } + elem = elem.nextSibling; + } + return false; + } + + function expand_all() { + var elem = document.checkboxform.firstChild; + while (elem != null) { + if (elem.firstChild != null) { + var tbody = elem.firstChild.nextSibling; + if (tbody.className == 'file_collapse') { + tbody.className = 'file'; + twisty = get_twisty_from_tbody(tbody); + twisty.firstChild.nodeValue = '(-)'; + twisty.nextSibling.checked = true; + } + } + elem = elem.nextSibling; + } + return false; + } + + var current_restore_elem; + + function restore_all() { + current_restore_elem = null; + incremental_restore(); + } + + function incremental_restore() { + if (!document.checkboxform.restore_indicator.checked) { + return; + } + var next_restore_elem; + if (current_restore_elem) { + next_restore_elem = current_restore_elem.nextSibling; + } else { + next_restore_elem = document.checkboxform.firstChild; + } + while (next_restore_elem != null) { + current_restore_elem = next_restore_elem; + if (current_restore_elem.firstChild != null) { + restore_elem(current_restore_elem.firstChild.nextSibling); + } + next_restore_elem = current_restore_elem.nextSibling; + } + } + + function restore_elem(elem, alertme) { + if (elem.className == 'file_collapse') { + twisty = get_twisty_from_tbody(elem); + if (twisty.nextSibling.checked) { + elem.className = 'file'; + twisty.firstChild.nodeValue = '(-)'; + } + } else if (elem.className == 'file') { + twisty = get_twisty_from_tbody(elem); + if (!twisty.nextSibling.checked) { + elem.className = 'file_collapse'; + twisty.firstChild.nodeValue = '(+)'; + } + } + } + + function twisty_click(twisty) { + tbody = get_tbody_from_twisty(twisty); + if (tbody.className == 'file') { + tbody.className = 'file_collapse'; + twisty.firstChild.nodeValue = '(+)'; + twisty.nextSibling.checked = false; + } else { + tbody.className = 'file'; + twisty.firstChild.nodeValue = '(-)'; + twisty.nextSibling.checked = true; + } + return false; + } + + function get_tbody_from_twisty(twisty) { + return twisty.parentNode.parentNode.parentNode.nextSibling; + } + function get_twisty_from_tbody(tbody) { + return tbody.previousSibling.firstChild.firstChild.firstChild; + } +[% END %] + +[% onload = 'restore_all(); document.checkboxform.restore_indicator.checked = true' %] + +[% IF headers %] + [% h1 = BLOCK %] + [% IF attachid %] + [% description FILTER html %] (#[% attachid %]) + [% ELSE %] + [% old_url = url('attachment.cgi', action = 'diff', id = oldid) %] + [% new_url = url('attachment.cgi', action = 'diff', id = newid) %] + Diff Between + [% old_desc FILTER html %] + (#[% oldid %]) + and + [% new_desc FILTER html %] + (#[% newid %]) + [% END %] + for Bug #[% bugid %] + [% END %] + [% h2 = BLOCK %] + [% bugsummary FILTER html %] + [% END %] + [% PROCESS global/header.html.tmpl %] +[% ELSE %] + + + + + + +[% END %] + +[%# If we have attachid, we are in diff, otherwise we're in interdiff %] +[% IF attachid %] + [%# HEADER %] + [% IF headers %] + [% USE url('attachment.cgi', id = attachid) %] + View + | Edit + [% USE url('attachment.cgi', id = attachid, context = context, + collapsed = collapsed, headers = headers, + action = 'diff') %] + | Raw Unified + [% END %] + [% IF other_patches %] + [% IF headers %] |[%END%] + Differences between +
+ + and this patch + + + + +
+ [% END %] +
+[% ELSE %] + [% IF headers %] + [% USE url('attachment.cgi', newid = newid, oldid = oldid, action = 'interdiff') %] + Raw Unified + [% IF attachid %] +
+ [% ELSE %] + | + [% END %] + [% END %] +[% END %] + +[%# Collapse / Expand %] +Collapse All | +Expand All + +[% IF do_context %] + | Context: + [% IF context == "patch" %] + (Patch / + [% ELSE %] + (Patch / + [% END %] + [% IF context == "file" %] + File / + [% ELSE %] + File / + [% END %] + + [% IF context == "patch" || context == "file" %] + [% context = 3 %] + [% END %] + [%# textbox for context %] +
) +[% END %] + +[% IF warning %] +

Warning: + [% IF warning == "interdiff1" %] + this difference between two patches may show things in the wrong places due + to a limitation in Bugzilla when comparing patches with different sets of + files. + [% END %] + [% IF warning == "interdiff2" %] + this difference between two patches may be inaccurate due to a limitation in + Bugzilla when comparing patches made against different revisions. + [% END %] +

+[% END %] + +[%# Restore Stuff %] +
+ + + diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index 14c2dc1fe..2cfc0e088 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -42,6 +42,10 @@ diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index fc5852923..598f8172b 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -69,8 +69,12 @@
[% IF attachment.canedit %] Edit - [% ELSE %] - None + [% END %] + [% IF attachment.ispatch %] + [% IF attachment.canedit %] + | + [% END %] + Diff [% END %]

- Attachment cannot be viewed because its MIME type is not either text/*, image/*, or application/vnd.mozilla.*. - Download the attachment instead. + Attachment is not viewable in your browser because its MIME type + ([% contenttype FILTER html %]) is not one that your browser is + able to display. +

+

+ Download + the attachment.

Attachment Type CreatedSizeFlags [% attachment.date FILTER time %][% attachment.datasize FILTER unitconvert %] @@ -82,7 +84,7 @@ [% END %]
+ Create a New Attachment (proposed patch, testcase, etc.)
- @@ -62,6 +62,7 @@ + diff --git a/template/en/default/filterexceptions.pl b/template/en/default/filterexceptions.pl index 1c74c3b89..c030fa04f 100644 --- a/template/en/default/filterexceptions.pl +++ b/template/en/default/filterexceptions.pl @@ -450,6 +450,7 @@ 'attachment/show-multiple.html.tmpl' => [ 'a.attachid', + 'flag.status' ], 'attachment/updated.html.tmpl' => [ -- cgit v1.2.3-65-gdbad From 6c0b6e11bf1e302320bebe0ce9545eff924ab124 Mon Sep 17 00:00:00 2001 From: "jouni%heikniemi.net" <> Date: Tue, 6 Jul 2004 14:08:02 +0000 Subject: Bug 223878: Flag system dies when changing a deleted flag. r=joel, justdave a=justdave --- Bugzilla/Attachment.pm | 3 ++- Bugzilla/Bug.pm | 9 +++++---- Bugzilla/Flag.pm | 39 +++++++++++++++++++++++++++++---------- Bugzilla/FlagType.pm | 2 ++ Bugzilla/Search.pm | 3 ++- attachment.cgi | 10 +++++++--- checksetup.pl | 7 +++++++ editflagtypes.cgi | 5 ++++- request.cgi | 3 +++ 9 files changed, 61 insertions(+), 20 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 9f0467bb7..e7b3ffe86 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -90,7 +90,8 @@ sub query $a{'datasize'}) = &::FetchSQLData(); # Retrieve a list of flags for this attachment. - $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); + $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'}, + 'is_active' => 1 }); # We will display the edit link if the user can edit the attachment; # ie the are the submitter, or they have canedit. diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 91cd0d8c8..f1a1cf341 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -226,7 +226,8 @@ sub initBug { $flag_type->{'flags'} = Bugzilla::Flag::match({ 'bug_id' => $self->{bug_id}, 'type_id' => $flag_type->{'id'}, - 'target_type' => 'bug' }); + 'target_type' => 'bug', + 'is_active' => 1 }); } $self->{'flag_types'} = $flag_types; $self->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types); @@ -238,11 +239,11 @@ sub initBug { my $num_attachment_flag_types = Bugzilla::FlagType::count({ 'target_type' => 'attachment', 'product_id' => $self->{'product_id'}, - 'component_id' => $self->{'component_id'}, - 'is_active' => 1 }); + 'component_id' => $self->{'component_id'} }); my $num_attachment_flags = Bugzilla::Flag::count({ 'target_type' => 'attachment', - 'bug_id' => $self->{bug_id} }); + 'bug_id' => $self->{bug_id}, + 'is_active' => 1 }); $self->{'show_attachment_flags'} = $num_attachment_flag_types || $num_attachment_flags; diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 3272b8409..707316ce1 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -18,6 +18,7 @@ # Rights Reserved. # # Contributor(s): Myk Melez +# Jouni Heikniemi ################################################################################ # Module Initialization @@ -52,8 +53,8 @@ use vars qw($template $vars); # basic sets of columns and tables for getting flags from the database my @base_columns = - ("1", "id", "type_id", "bug_id", "attach_id", "requestee_id", "setter_id", - "status"); + ("is_active", "id", "type_id", "bug_id", "attach_id", "requestee_id", + "setter_id", "status"); # Note: when adding tables to @base_tables, make sure to include the separator # (i.e. a comma or words like "LEFT OUTER JOIN") before the table name, @@ -154,6 +155,11 @@ sub validate { my $flag = get($id); $flag || ThrowCodeError("flag_nonexistent", { id => $id }); + # Note that the deletedness of the flag (is_active or not) is not + # checked here; we do want to allow changes to deleted flags in + # certain cases. Flag::modify() will revive the modified flags. + # See bug 223878 for details. + # Make sure the user chose a valid status. grep($status eq $_, qw(X + - ?)) || ThrowCodeError("flag_status_invalid", @@ -226,7 +232,8 @@ sub process { # Take a snapshot of flags before any changes. my $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} , - 'attach_id' => $target->{'attachment'}->{'id'} }); + 'attach_id' => $target->{'attachment'}->{'id'} , + 'is_active' => 1 }); my @old_summaries; foreach my $flag (@$flags) { my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; @@ -249,6 +256,7 @@ sub process { AND (bugs.product_id = i.product_id OR i.product_id IS NULL) AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) WHERE bugs.bug_id = $target->{'bug'}->{'id'} + AND flags.is_active = 1 AND i.type_id IS NULL "); clear(&::FetchOneColumn()) while &::MoreSQLData(); @@ -257,7 +265,8 @@ sub process { FROM flags, bugs, flagexclusions e WHERE bugs.bug_id = $target->{'bug'}->{'id'} AND flags.bug_id = bugs.bug_id - AND flags.type_id = e.type_id + AND flags.type_id = e.type_id + AND flags.is_active = 1 AND (bugs.product_id = e.product_id OR e.product_id IS NULL) AND (bugs.component_id = e.component_id OR e.component_id IS NULL) "); @@ -265,7 +274,8 @@ sub process { # Take a snapshot of flags after changes. $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} , - 'attach_id' => $target->{'attachment'}->{'id'} }); + 'attach_id' => $target->{'attachment'}->{'id'} , + 'is_active' => 1 }); my @new_summaries; foreach my $flag (@$flags) { my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; @@ -340,6 +350,9 @@ sub migrate { sub modify { # Modifies flags in the database when a user changes them. + # Note that modified flags are always set active (is_active = 1) - + # this will revive deleted flags that get changed through + # attachment.cgi midairs. See bug 223878 for details. my ($data, $timestamp) = @_; @@ -385,7 +398,8 @@ sub modify { SET setter_id = $::userid , requestee_id = NULL , status = '$status' , - modification_date = $timestamp + modification_date = $timestamp , + is_active = 1 WHERE id = $flag->{'id'}"); # Send an email notifying the relevant parties about the fulfillment. @@ -409,7 +423,8 @@ sub modify { SET setter_id = $::userid , requestee_id = $requestee_id , status = '$status' , - modification_date = $timestamp + modification_date = $timestamp , + is_active = 1 WHERE id = $flag->{'id'}"); # Send an email notifying the relevant parties about the request. @@ -420,7 +435,7 @@ sub modify { notify($flag, "request/email.txt.tmpl"); } } - # The user unset the flag, so delete it from the database. + # The user unset the flag; set is_active = 0 elsif ($status eq 'X') { clear($flag->{'id'}); } @@ -437,9 +452,10 @@ sub clear { my $flag = get($id); &::PushGlobalSQLState(); - &::SendSQL("DELETE FROM flags WHERE id = $id"); + &::SendSQL("UPDATE flags SET is_active = 0 WHERE id = $id"); &::PopGlobalSQLState(); - + + $flag->{'exists'} = 0; # Set the flag's status to "cleared" so the email template # knows why email is being sent about the request. $flag->{'status'} = "X"; @@ -625,6 +641,7 @@ sub sqlify_criteria { elsif ($field eq 'requestee_id') { push(@criteria, "requestee_id = $value") } elsif ($field eq 'setter_id') { push(@criteria, "setter_id = $value") } elsif ($field eq 'status') { push(@criteria, "status = '$value'") } + elsif ($field eq 'is_active') { push(@criteria, "is_active = $value") } } return @criteria; @@ -635,6 +652,8 @@ sub perlify_record { my ($exists, $id, $type_id, $bug_id, $attach_id, $requestee_id, $setter_id, $status) = @_; + return undef unless defined($exists); + my $flag = { exists => $exists , diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index e6bfaf7ef..a428d5389 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -270,6 +270,7 @@ sub normalize { AND (bugs.product_id = i.product_id OR i.product_id IS NULL) AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) WHERE flags.type_id IN ($ids) + AND flags.is_active = 1 AND i.type_id IS NULL "); Bugzilla::Flag::clear(&::FetchOneColumn()) while &::MoreSQLData(); @@ -280,6 +281,7 @@ sub normalize { WHERE flags.type_id IN ($ids) AND flags.bug_id = bugs.bug_id AND flags.type_id = e.type_id + AND flags.is_active = 1 AND (bugs.product_id = e.product_id OR e.product_id IS NULL) AND (bugs.component_id = e.component_id OR e.component_id IS NULL) "); diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 2f92131fc..0ebf1e070 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -586,7 +586,8 @@ sub init { # negative conditions (f.e. "flag isn't review+"). my $flags = "flags_$chartid"; push(@supptables, "LEFT JOIN flags $flags " . - "ON bugs.bug_id = $flags.bug_id"); + "ON bugs.bug_id = $flags.bug_id " . + "AND $flags.is_active = 1"); my $flagtypes = "flagtypes_$chartid"; push(@supptables, "LEFT JOIN flagtypes $flagtypes " . "ON $flags.type_id = $flagtypes.id"); diff --git a/attachment.cgi b/attachment.cgi index c1e8f9dd0..06a04291e 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -768,7 +768,8 @@ sub viewall $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $a{'datasize'}) = FetchSQLData(); $a{'isviewable'} = isViewable($a{'contenttype'}); - $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); + $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'}, + 'is_active' => 1 }); # Add the hash representing the attachment to the array of attachments. push @attachments, \%a; @@ -880,7 +881,9 @@ sub insert SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($::FORM{'bugid'}, $obsolete_id, $::userid, NOW(), $fieldid, '0', '1')"); # If the obsolete attachment has pending flags, migrate them to the new attachment. - if (Bugzilla::Flag::count({ 'attach_id' => $obsolete_id , 'status' => 'pending' })) { + if (Bugzilla::Flag::count({ 'attach_id' => $obsolete_id , + 'status' => 'pending', + 'is_active' => 1 })) { Bugzilla::Flag::migrate($obsolete_id, $attachid); } } @@ -984,7 +987,8 @@ sub edit 'component_id' => $component_id }); foreach my $flag_type (@$flag_types) { $flag_type->{'flags'} = Bugzilla::Flag::match({ 'type_id' => $flag_type->{'id'}, - 'attach_id' => $::FORM{'id'} }); + 'attach_id' => $::FORM{'id'}, + 'is_active' => 1 }); } $vars->{'flag_types'} = $flag_types; $vars->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types); diff --git a/checksetup.pl b/checksetup.pl index 684a8ca7e..3c4793407 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1609,6 +1609,8 @@ $table{flags} = setter_id MEDIUMINT NULL , requestee_id MEDIUMINT NULL , + + is_active TINYINT NOT NULL DEFAULT 1, INDEX(bug_id, attach_id) , INDEX(setter_id) , @@ -3935,6 +3937,11 @@ if (GetFieldDef("user_group_map", "isderived")) { } } +# 2004-07-03 - Make it possible to disable flags without deleting them +# from the database. Bug 223878, jouni@heikniemi.net + +AddField('flags', 'is_active', 'tinyint not null default 1'); + # If you had to change the --TABLE-- definition in any way, then add your diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 5fcabd73f..bb1b86ec0 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -308,6 +308,7 @@ sub update { AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) WHERE flags.type_id = $::FORM{'id'} AND flags.bug_id = bugs.bug_id + AND flags.is_active = 1 AND i.type_id IS NULL "); Bugzilla::Flag::clear(FetchOneColumn()) while MoreSQLData(); @@ -318,6 +319,7 @@ sub update { WHERE flags.type_id = $::FORM{'id'} AND flags.bug_id = bugs.bug_id AND flags.type_id = e.type_id + AND flags.is_active = 1 AND (bugs.product_id = e.product_id OR e.product_id IS NULL) AND (bugs.component_id = e.component_id OR e.component_id IS NULL) "); @@ -340,7 +342,8 @@ sub confirmDelete validateID(); # check if we need confirmation to delete: - my $count = Bugzilla::Flag::count({ 'type_id' => $::FORM{'id'} }); + my $count = Bugzilla::Flag::count({ 'type_id' => $::FORM{'id'}, + 'is_active' => 1 }); if ($count > 0) { $vars->{'flag_type'} = Bugzilla::FlagType::get($::FORM{'id'}); diff --git a/request.cgi b/request.cgi index e330c2c83..047c4fa14 100755 --- a/request.cgi +++ b/request.cgi @@ -116,6 +116,9 @@ sub queue { AND flags.bug_id = bugs.bug_id "; + # Non-deleted flags only + $query .= " AND flags.is_active = 1 "; + # Limit query to pending requests. $query .= " AND flags.status = '?' " unless $cgi->param('status'); -- cgit v1.2.3-65-gdbad From 8c96e74e3dbb34cdc97282e91f019dfed8df1405 Mon Sep 17 00:00:00 2001 From: "kiko%async.com.br" <> Date: Thu, 22 Jul 2004 00:53:34 +0000 Subject: Fix for bug 252378: Remove $COOKIE from attachment.cgi. r=bugreport, a=justdave. --- attachment.cgi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 06a04291e..ca4380902 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -870,7 +870,7 @@ sub insert $comment = Text::Wrap::wrap('', '', $comment); AppendComment($::FORM{'bugid'}, - $::COOKIE{"Bugzilla_login"}, + Bugzilla->user->login, $comment, $isprivate); @@ -934,7 +934,7 @@ sub insert } # Define the variables and functions that will be passed to the UI template. - $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'}, + $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login, 'owner' => $owner }; my $bugid = $::FORM{'bugid'}; detaint_natural($bugid); # don't bother with error condition, we know it'll work @@ -1156,7 +1156,7 @@ sub update } # Define the variables and functions that will be passed to the UI template. - $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} }; + $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login }; $vars->{'attachid'} = $::FORM{'id'}; $vars->{'bugid'} = $bugid; -- cgit v1.2.3-65-gdbad From 419a4d9fc5fd0872d662fe3793ed15288b74b3ba Mon Sep 17 00:00:00 2001 From: "kiko%async.com.br" <> Date: Mon, 26 Jul 2004 08:27:10 +0000 Subject: Fix for bug 251911: Silly ThrowUserError bits in attachment.cgi. Fixing variables missing in some errors raised, and doing bits of $::FORM cleanup while we're at it. r=joel, a=justdave. --- attachment.cgi | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index ca4380902..8518d0278 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -166,58 +166,64 @@ sub validateID { my $param = @_ ? $_[0] : 'id'; - # Only do this check for no 'id' parameter if we are trying to - # validate the 'id' parameter + # If we're not doing interdiffs, check if id wasn't specified and + # prompt them with a page that allows them to choose an attachment. + # Happens when calling plain attachment.cgi from the urlbar directly if ($param eq 'id' && !$cgi->param('id')) { print Bugzilla->cgi->header(); $template->process("attachment/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; - } - - # Validate the value of the "id" form field, which must contain an - # integer that is the ID of an existing attachment. - - $vars->{'attach_id'} = $::FORM{$param}; - detaint_natural($::FORM{$param}) - || ThrowUserError("invalid_attach_id"); + my $attach_id = $cgi->param($param); + + # Validate the specified attachment id. detaint kills $attach_id if + # non-natural, so use the original value from $cgi in our exception + # message here. + detaint_natural($attach_id) + || ThrowUserError("invalid_attach_id", { attach_id => $cgi->param($param) }); # Make sure the attachment exists in the database. - SendSQL("SELECT bug_id, isprivate FROM attachments WHERE attach_id = $::FORM{$param}"); + SendSQL("SELECT bug_id, isprivate FROM attachments WHERE attach_id = $attach_id"); MoreSQLData() - || ThrowUserError("invalid_attach_id"); + || ThrowUserError("invalid_attach_id", { attach_id => $attach_id }); # Make sure the user is authorized to access this attachment's bug. ($bugid, my $isprivate) = FetchSQLData(); ValidateBugID($bugid); - if (($isprivate > 0 ) && Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { + if (($isprivate > 0 ) && Param("insidergroup") && + !(UserInGroup(Param("insidergroup")))) { ThrowUserError("attachment_access_denied"); } + + # XXX shim code, kill $::FORM + $::FORM{$param} = $attach_id; } sub validateFormat { - $::FORM{'format'} ||= $_[0]; - if (! grep { $_ eq $::FORM{'format'} } @_) + # receives a list of legal formats; first item is a default + my $format = $cgi->param('format') || $_[0]; + if ( lsearch(\@_, $format) == -1) { - $vars->{'format'} = $::FORM{'format'}; - $vars->{'formats'} = \@_; - ThrowUserError("invalid_format"); + ThrowUserError("invalid_format", { format => $format, formats => \@_ }); } + + # XXX shim code, kill $::FORM + $::FORM{'format'} = $format; } sub validateContext { - $::FORM{'context'} ||= "patch"; - if ($::FORM{'context'} ne "file" && $::FORM{'context'} ne "patch") { - $vars->{'context'} = $::FORM{'context'}; - detaint_natural($::FORM{'context'}) - || ThrowUserError("invalid_context"); - delete $vars->{'context'}; + my $context = $cgi->param('context') || "patch"; + if ($context ne "file" && $context ne "patch") { + detaint_natural($context) + || ThrowUserError("invalid_context", { context => $cgi->param('context') }); } + # XXX shim code, kill $::FORM + $::FORM{'context'} = $context; } sub validateCanEdit -- cgit v1.2.3-65-gdbad From fcd317eee72a7eb2d439a68e47e988997d709b25 Mon Sep 17 00:00:00 2001 From: "jocuri%softhome.net" <> Date: Wed, 1 Sep 2004 06:28:09 +0000 Subject: Patch for bug 254371: include Flag Types in create new attachments form; patch by Alexandre Michetti Manduca ; r=jouni, a=myk. --- attachment.cgi | 26 +++++++++++++++++++++++++ template/en/default/attachment/create.html.tmpl | 14 +++++++++++++ 2 files changed, 40 insertions(+) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 8518d0278..96a8ba6f8 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -120,6 +120,15 @@ elsif ($action eq "insert") validateDescription(); validateContentType() unless $::FORM{'ispatch'}; validateObsolete() if $::FORM{'obsolete'}; + + # The order of these function calls is important, as both Flag::validate + # and FlagType::validate assume User::match_field has ensured that the values + # in the requestee fields are legitimate user email addresses. + Bugzilla::User::match_field({ '^requestee(_type)?-(\d+)$' => + { 'type' => 'single' } }); + Bugzilla::Flag::validate(\%::FORM, $bugid); + Bugzilla::FlagType::validate(\%::FORM, $bugid, $::FORM{'id'}); + insert($data); } elsif ($action eq "edit") @@ -837,6 +846,16 @@ sub enter $vars->{'bugsummary'} = $bugsummary; $vars->{'GetBugLink'} = \&GetBugLink; + SendSQL("SELECT product_id, component_id FROM bugs + WHERE bug_id = $::FORM{'bugid'}"); + my ($product_id, $component_id) = FetchSQLData(); + my $flag_types = Bugzilla::FlagType::match({'target_type' => 'attachment', + 'product_id' => $product_id, + 'component_id' => $component_id}); + $vars->{'flag_types'} = $flag_types; + $vars->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, + @$flag_types); + print Bugzilla->cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. @@ -939,6 +958,13 @@ sub insert } } + # Figure out when the changes were made. + my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()"); + + # Create flags. + my $target = Bugzilla::Flag::GetTarget(undef, $attachid); + Bugzilla::Flag::process($target, $timestamp, \%::FORM); + # Define the variables and functions that will be passed to the UI template. $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login, 'owner' => $owner }; diff --git a/template/en/default/attachment/create.html.tmpl b/template/en/default/attachment/create.html.tmpl index e97812181..3089c0d5f 100644 --- a/template/en/default/attachment/create.html.tmpl +++ b/template/en/default/attachment/create.html.tmpl @@ -43,6 +43,12 @@ vertical-align: baseline; padding-bottom: 5px; } + + table#flags th, table#flags td { + text-align: left; + vertical-align: baseline; + font-size: small; + } " onload="setContentTypeDisabledState();" %] @@ -137,6 +143,14 @@ [% END %] + + + +
+ Attachment #[% a.attachid %]
[% a.date FILTER time %][% a.datasize FILTER unitconvert %] [% IF a.statuses.size == 0 %] -- cgit v1.2.3-65-gdbad From 4df1c8fd665e5fc7c66e265b1f32b75837ae719f Mon Sep 17 00:00:00 2001 From: "kiko%async.com.br" <> Date: Sat, 27 Mar 2004 11:51:43 +0000 Subject: Fix for bug 234175: Remove deprecated ConnectToDatabase() and quietly_check_login()/confirm_login() calls. Cleans up callsites (consisting of most of our CGIs), swapping (where appropriate) for calls to Bugzilla->login. Patch by Teemu Mannermaa . r=bbaetz, kiko. a=justdave. --- Bugzilla/DB.pm | 10 +--------- CGI.pl | 8 -------- attachment.cgi | 18 ++++++++---------- buglist.cgi | 15 +++++++-------- chart.cgi | 5 ++--- colchange.cgi | 3 +-- collectstats.pl | 1 - config.cgi | 4 ---- contrib/bug_email.pl | 4 +--- contrib/bugzilla_email_append.pl | 2 -- contrib/syncLDAP.pl | 2 -- createaccount.cgi | 4 +--- describecomponents.cgi | 7 +++---- describekeywords.cgi | 4 +--- doeditparams.cgi | 4 ++-- duplicates.cgi | 3 +-- editcomponents.cgi | 4 ++-- editflagtypes.cgi | 6 ++---- editgroups.cgi | 3 +-- editkeywords.cgi | 4 ++-- editmilestones.cgi | 4 ++-- editparams.cgi | 4 ++-- editproducts.cgi | 3 +-- editusers.cgi | 4 ++-- editversions.cgi | 4 ++-- enter_bug.cgi | 11 +++-------- importxml.pl | 1 - index.cgi | 6 ++---- long_list.cgi | 4 +--- move.pl | 4 ++-- page.cgi | 4 +--- post_bug.cgi | 3 +-- process_bug.cgi | 3 +-- query.cgi | 8 ++++---- queryhelp.cgi | 3 +-- quips.cgi | 5 +++-- relogin.cgi | 6 ++---- report.cgi | 5 ++--- reports.cgi | 3 +-- request.cgi | 5 +---- sanitycheck.cgi | 4 +--- show_activity.cgi | 4 +--- show_bug.cgi | 7 +++---- showdependencygraph.cgi | 4 +--- showdependencytree.cgi | 4 +--- sidebar.cgi | 3 +-- token.cgi | 6 +++--- userprefs.cgi | 3 +-- votes.cgi | 7 +++---- whineatnews.pl | 2 -- 50 files changed, 84 insertions(+), 161 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index a747aebd6..684869006 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -33,7 +33,7 @@ use base qw(Exporter); %Bugzilla::DB::EXPORT_TAGS = ( - deprecated => [qw(ConnectToDatabase SendSQL SqlQuote + deprecated => [qw(SendSQL SqlQuote MoreSQLData FetchSQLData FetchOneColumn PushGlobalSQLState PopGlobalSQLState) ], @@ -49,10 +49,6 @@ use Bugzilla::Util; # having a separate package for it, or otherwise trying to avoid the circular # dependancy -sub ConnectToDatabase { - # We've already been connected in Bugzilla.pm -} - # XXX - mod_perl # These use |our| instead of |my| because they need to be cleared from # Bugzilla.pm. See bug 192531 for details. @@ -222,10 +218,6 @@ and so are not documented. =item * -ConnectToDatabase - -=item * - SendSQL =item * diff --git a/CGI.pl b/CGI.pl index 982b067b2..6d10268d3 100644 --- a/CGI.pl +++ b/CGI.pl @@ -198,10 +198,6 @@ sub PasswordForLogin { return $result; } -sub quietly_check_login { - return Bugzilla->login($_[0] ? LOGIN_OPTIONAL : LOGIN_NORMAL); -} - sub CheckEmailSyntax { my ($addr) = (@_); my $match = Param('emailregexp'); @@ -224,10 +220,6 @@ sub MailPassword { close SENDMAIL; } -sub confirm_login { - return Bugzilla->login(LOGIN_REQUIRED); -} - sub PutHeader { ($vars->{'title'}, $vars->{'h1'}, $vars->{'h2'}) = (@_); diff --git a/attachment.cgi b/attachment.cgi index 8df562120..61565f01f 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -42,16 +42,14 @@ use vars qw( require "CGI.pl"; # Use these modules to handle flags. +use Bugzilla::Constants; use Bugzilla::Flag; use Bugzilla::FlagType; use Bugzilla::User; use Bugzilla::Util; -# Establish a connection to the database backend. -ConnectToDatabase(); - # Check whether or not the user is logged in and, if so, set the $::userid -quietly_check_login(); +Bugzilla->login(); # The ID of the bug to which the attachment is attached. Gets set # by validateID() (which validates the attachment ID, not the bug ID, but has @@ -104,14 +102,14 @@ elsif ($action eq "viewall") } elsif ($action eq "enter") { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); ValidateBugID($::FORM{'bugid'}); validateCanChangeBug($::FORM{'bugid'}); enter(); } elsif ($action eq "insert") { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); ValidateBugID($::FORM{'bugid'}); validateCanChangeBug($::FORM{'bugid'}); ValidateComment($::FORM{'comment'}); @@ -125,14 +123,13 @@ elsif ($action eq "insert") } elsif ($action eq "edit") { - quietly_check_login(); validateID(); validateCanEdit($::FORM{'id'}); edit(); } elsif ($action eq "update") { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); ValidateComment($::FORM{'comment'}); validateID(); validateCanEdit($::FORM{'id'}); @@ -216,9 +213,10 @@ sub validateCanEdit my ($attach_id) = (@_); # If the user is not logged in, claim that they can edit. This allows - # the edit scrren to be displayed to people who aren't logged in. + # the edit screen to be displayed to people who aren't logged in. # People not logged in can't actually commit changes, because that code - # calls confirm_login, not quietly_check_login, before calling this sub + # calls Bugzilla->login with LOGIN_REQUIRED, not with LOGIN_NORMAL, + # before calling this sub return if $::userid == 0; # People in editbugs can edit all attachments diff --git a/buglist.cgi b/buglist.cgi index 8a02ca490..2a71450aa 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -37,6 +37,7 @@ use vars qw($template $vars); use Bugzilla; use Bugzilla::Search; +use Bugzilla::Constants; # Include the Bugzilla CGI and general utility library. require "CGI.pl"; @@ -63,8 +64,6 @@ if (length($::buffer) == 0) { ThrowUserError("buglist_parameters_required"); } -ConnectToDatabase(); - ################################################################################ # Data and Security Validation ################################################################################ @@ -74,12 +73,12 @@ my $dotweak = $::FORM{'tweak'} ? 1 : 0; # Log the user in if ($dotweak) { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); UserInGroup("editbugs") || ThrowUserError("insufficient_privs_for_multi"); GetVersionTable(); } else { - quietly_check_login(); + Bugzilla->login(); } # Hack to support legacy applications that think the RDF ctype is at format=rdf. @@ -182,7 +181,7 @@ sub iCalendarDateTime { sub LookupNamedQuery { my ($name) = @_; - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); my $qname = SqlQuote($name); SendSQL("SELECT query FROM namedqueries WHERE userid = $userid AND name = $qname"); @@ -305,7 +304,7 @@ if ($::FORM{'cmdtype'} eq "dorem") { $order = $params->param('order') || $order; } elsif ($::FORM{'remaction'} eq "forget") { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); my $qname = SqlQuote($::FORM{'namedcmd'}); SendSQL("DELETE FROM namedqueries WHERE userid = $userid AND name = $qname"); @@ -325,7 +324,7 @@ if ($::FORM{'cmdtype'} eq "dorem") { } elsif (($::FORM{'cmdtype'} eq "doit") && $::FORM{'remtype'}) { if ($::FORM{'remtype'} eq "asdefault") { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); my $qname = SqlQuote($::defaultqueryname); my $qbuffer = SqlQuote($::buffer); @@ -335,7 +334,7 @@ elsif (($::FORM{'cmdtype'} eq "doit") && $::FORM{'remtype'}) { $vars->{'message'} = "buglist_new_default_query"; } elsif ($::FORM{'remtype'} eq "asnamed") { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); my $name = trim($::FORM{'newqueryname'}); diff --git a/chart.cgi b/chart.cgi index dbdd818bc..229e9bbf7 100755 --- a/chart.cgi +++ b/chart.cgi @@ -45,6 +45,7 @@ use strict; use lib qw(.); require "CGI.pl"; +use Bugzilla::Constants; use Bugzilla::Chart; use Bugzilla::Series; @@ -81,9 +82,7 @@ if ($action eq "search") { exit; } -ConnectToDatabase(); - -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); # Only admins may create public queries UserInGroup('admin') || $cgi->delete('public'); diff --git a/colchange.cgi b/colchange.cgi index 726e60d51..dcd611dad 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -36,8 +36,7 @@ use Bugzilla; require "CGI.pl"; -ConnectToDatabase(); -quietly_check_login(); +Bugzilla->login(); GetVersionTable(); diff --git a/collectstats.pl b/collectstats.pl index e04169453..7391d32da 100755 --- a/collectstats.pl +++ b/collectstats.pl @@ -51,7 +51,6 @@ if (chdir("graphs")) { chdir(".."); } -ConnectToDatabase(); GetVersionTable(); Bugzilla->switch_to_shadow_db(); diff --git a/config.cgi b/config.cgi index f7cb95ee5..a2c22d001 100755 --- a/config.cgi +++ b/config.cgi @@ -33,10 +33,6 @@ use strict; use lib qw(.); require "CGI.pl"; -# Connect to the database so we can check whether the user is a member -# of each product group. -ConnectToDatabase(); - # Retrieve this installation's configuration. GetVersionTable(); diff --git a/contrib/bug_email.pl b/contrib/bug_email.pl index a8b89714d..a4bae060c 100755 --- a/contrib/bug_email.pl +++ b/contrib/bug_email.pl @@ -38,7 +38,7 @@ # # You need to work with bug_email.pl the MIME::Parser installed. # -# $Id: bug_email.pl,v 1.18 2004/01/20 06:03:38 justdave%syndicomm.com Exp $ +# $Id: bug_email.pl,v 1.19 2004/03/27 03:51:44 kiko%async.com.br Exp $ ############################################################### # 02/12/2000 (SML) @@ -746,8 +746,6 @@ die (" *** Cant find Sender-adress in sent mail ! ***\n" ) unless defined( $Send chomp( $Sender ); chomp( $Message_ID ); -ConnectToDatabase(); - $SenderShort = $Sender; $SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/; diff --git a/contrib/bugzilla_email_append.pl b/contrib/bugzilla_email_append.pl index da098e66c..007fd153c 100755 --- a/contrib/bugzilla_email_append.pl +++ b/contrib/bugzilla_email_append.pl @@ -68,8 +68,6 @@ chomp( $Message_ID ); print "Dealing with the sender $Sender\n"; -ConnectToDatabase(); - my $SenderShort = $Sender; $SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/; diff --git a/contrib/syncLDAP.pl b/contrib/syncLDAP.pl index 701695aea..b9d3e8a5f 100755 --- a/contrib/syncLDAP.pl +++ b/contrib/syncLDAP.pl @@ -74,8 +74,6 @@ foreach my $arg (@ARGV) } } -ConnectToDatabase(); - my %bugzilla_users; my %ldap_users; diff --git a/createaccount.cgi b/createaccount.cgi index 6c624b0ba..6364e20bc 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -36,13 +36,11 @@ use vars qw( $vars ); -ConnectToDatabase(); - # If we're using LDAP for login, then we can't create a new account here. unless (Bugzilla::Auth->can_edit) { # Just in case someone already has an account, let them get the correct # footer on the error message - quietly_check_login(); + Bugzilla->login(); ThrowUserError("auth_cant_create_account"); } diff --git a/describecomponents.cgi b/describecomponents.cgi index 1926a8826..6c99a0a63 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -23,7 +23,6 @@ use vars qw( %legal_product - $userid ); use strict; @@ -31,11 +30,11 @@ use strict; use lib qw(.); use Bugzilla; +use Bugzilla::Constants; require "CGI.pl"; -ConnectToDatabase(); -quietly_check_login(); +Bugzilla->login(); GetVersionTable(); @@ -48,7 +47,7 @@ if (!defined $product) { if (AnyEntryGroups()) { # OK, now only add products the user can see - confirm_login() unless $::userid; + Bugzilla->login(LOGIN_REQUIRED) unless Bugzilla->user; foreach my $p (@::legal_product) { if (CanEnterProduct($p)) { $products{$p} = $::proddesc{$p}; diff --git a/describekeywords.cgi b/describekeywords.cgi index 60c5a9fd8..8597e6791 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -31,9 +31,7 @@ require "CGI.pl"; # Use the global template variables. use vars qw($vars $template); -ConnectToDatabase(); - -quietly_check_login(); +Bugzilla->login(); my $cgi = Bugzilla->cgi; diff --git a/doeditparams.cgi b/doeditparams.cgi index 4799c99c0..679bd74e3 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -26,12 +26,12 @@ use strict; use lib qw(.); use Bugzilla; +use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT :admin $datadir); require "CGI.pl"; -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; diff --git a/duplicates.cgi b/duplicates.cgi index aa627fc40..b45bd2710 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -54,7 +54,6 @@ if (defined $cgi->param('ctype') && $cgi->param('ctype') eq "xul") { # Use global templatisation variables. use vars qw($template $vars); -ConnectToDatabase(); GetVersionTable(); # collectstats.pl uses duplicates.cgi to generate the RDF duplicates stats. @@ -64,7 +63,7 @@ if ($::ENV{'GATEWAY_INTERFACE'} eq "cmdline") { Bugzilla->login(LOGIN_OPTIONAL); } else { - Bugzilla->login(LOGIN_NORMAL); + Bugzilla->login(); } Bugzilla->switch_to_shadow_db(); diff --git a/editcomponents.cgi b/editcomponents.cgi index 1cac27a99..5ed7a28d7 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -31,6 +31,7 @@ use lib "."; require "CGI.pl"; require "globals.pl"; +use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Series; @@ -195,8 +196,7 @@ sub PutTrailer (@) # Preliminary checks: # -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 711828b6a..5fcabd73f 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -31,18 +31,16 @@ use lib "."; # Include the Bugzilla CGI and general utility library. require "CGI.pl"; -# Establish a connection to the database backend. -ConnectToDatabase(); - # Use Bugzilla's flag modules for handling flag types. use Bugzilla; +use Bugzilla::Constants; use Bugzilla::Flag; use Bugzilla::FlagType; use vars qw( $template $vars ); # Make sure the user is logged in and is an administrator. -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); UserInGroup("editcomponents") || ThrowUserError("authorization_failure", { action => "administer flag types" }); diff --git a/editgroups.cgi b/editgroups.cgi index e96545768..4f97972b3 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -34,8 +34,7 @@ require "CGI.pl"; use vars qw($template $vars); -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editkeywords.cgi b/editkeywords.cgi index 0069886cd..0083f8ee9 100755 --- a/editkeywords.cgi +++ b/editkeywords.cgi @@ -25,6 +25,7 @@ use lib "."; require "CGI.pl"; +use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); my $cgi = Bugzilla->cgi; @@ -53,8 +54,7 @@ sub Validate ($$) { # Preliminary checks: # -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editmilestones.cgi b/editmilestones.cgi index 7a77de155..e707cf46d 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -19,6 +19,7 @@ use lib "."; require "CGI.pl"; require "globals.pl"; +use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); # TestProduct: just returns if the specified product does exists @@ -144,8 +145,7 @@ sub PutTrailer (@) # Preliminary checks: # -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editparams.cgi b/editparams.cgi index dd61e9543..aaa2b087a 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -25,12 +25,12 @@ use strict; use lib "."; +use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT :admin); require "CGI.pl"; -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editproducts.cgi b/editproducts.cgi index f53779501..051fdb86c 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -179,8 +179,7 @@ sub PutTrailer (@) # Preliminary checks: # -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editusers.cgi b/editusers.cgi index e7ef0e7d3..3db5aef86 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -36,6 +36,7 @@ require "globals.pl"; use Bugzilla; use Bugzilla::User; +use Bugzilla::Constants; # Shut up misguided -w warnings about "used only once". "use vars" just # doesn't work for me. @@ -238,8 +239,7 @@ sub PutTrailer (@) # Preliminary checks: # -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/editversions.cgi b/editversions.cgi index 9c4a5e5ea..527f42aaf 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -32,6 +32,7 @@ use lib "."; require "CGI.pl"; require "globals.pl"; +use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); # TestProduct: just returns if the specified product does exists @@ -153,8 +154,7 @@ sub PutTrailer (@) # Preliminary checks: # -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); diff --git a/enter_bug.cgi b/enter_bug.cgi index eca672a45..1dd02c90c 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -57,20 +57,15 @@ use vars qw( $proddesc ); -# We have to connect to the database, even though we don't use it in this code, -# because we might occasionally rebuild the version cache, which causes tokens -# to get deleted from the database, which needs a database connection. -ConnectToDatabase(); - # If we're using bug groups to restrict bug entry, we need to know who the # user is right from the start. -confirm_login() if AnyEntryGroups(); +Bugzilla->login(LOGIN_REQUIRED) if AnyEntryGroups(); my $cgi = Bugzilla->cgi; if (!defined $::FORM{'product'}) { GetVersionTable(); - quietly_check_login(); + Bugzilla->login(); my %products; @@ -225,7 +220,7 @@ sub pickos { # End of subroutines ############################################################################## -confirm_login() if (!(AnyEntryGroups())); +Bugzilla->login(LOGIN_REQUIRED) if (!(AnyEntryGroups())); # We need to check and make sure # that the user has permission to enter a bug against this product. diff --git a/importxml.pl b/importxml.pl index 5b0599e98..f23387176 100755 --- a/importxml.pl +++ b/importxml.pl @@ -71,7 +71,6 @@ require "CGI.pl"; require "globals.pl"; $::lockcount = 0; -ConnectToDatabase(); GetVersionTable(); diff --git a/index.cgi b/index.cgi index bbe936207..88393b417 100755 --- a/index.cgi +++ b/index.cgi @@ -36,11 +36,9 @@ use vars qw( $vars ); -# Establish a connection to the database backend. -ConnectToDatabase(); - # Check whether or not the user is logged in and, if so, set the $::userid -quietly_check_login('permit_anonymous'); +use Bugzilla::Constants; +Bugzilla->login(LOGIN_OPTIONAL); ############################################################################### # Main Body Execution diff --git a/long_list.cgi b/long_list.cgi index 72c579ac4..657ff9e24 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -33,9 +33,7 @@ use vars qw($userid @legal_keywords); # Use global template variables. use vars qw($template $vars); -ConnectToDatabase(); - -quietly_check_login(); +Bugzilla->login(); GetVersionTable(); diff --git a/move.pl b/move.pl index b4d47a40f..99ed585f3 100755 --- a/move.pl +++ b/move.pl @@ -31,6 +31,7 @@ require "CGI.pl"; use vars qw($template $userid %COOKIE); use Bugzilla; +use Bugzilla::Constants; use Bugzilla::Bug; use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::BugMail; @@ -43,8 +44,7 @@ unless ( Param("move-enabled") ) { exit; } -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; diff --git a/page.cgi b/page.cgi index 91027ff4f..b68a9313b 100755 --- a/page.cgi +++ b/page.cgi @@ -38,9 +38,7 @@ require "CGI.pl"; use vars qw($template $vars); -ConnectToDatabase(); - -quietly_check_login(); +Bugzilla->login(); my $cgi = Bugzilla->cgi; diff --git a/post_bug.cgi b/post_bug.cgi index d6fda9b3b..800b46f01 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -53,8 +53,7 @@ sub sillyness { # Use global template variables. use vars qw($vars $template); -ConnectToDatabase(); -my $user = confirm_login(); +my $user = Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; diff --git a/process_bug.cgi b/process_bug.cgi index 4df90efd2..e54a46965 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -55,8 +55,7 @@ use vars qw(%versions %legal_severity ); -ConnectToDatabase(); -my $user = confirm_login(); +my $user = Bugzilla->login(LOGIN_REQUIRED); my $whoid = $user->id; my $cgi = Bugzilla->cgi; diff --git a/query.cgi b/query.cgi index 7786da96b..a37fc9125 100755 --- a/query.cgi +++ b/query.cgi @@ -28,6 +28,8 @@ use lib "."; require "CGI.pl"; +use Bugzilla::Constants; + use vars qw( @CheckOptionValues @legal_resolution @@ -49,16 +51,14 @@ use vars qw( $vars ); -ConnectToDatabase(); - my $cgi = Bugzilla->cgi; if (defined $::FORM{"GoAheadAndLogIn"}) { # We got here from a login page, probably from relogin.cgi. We better # make sure the password is legit. - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); } else { - quietly_check_login(); + Bugzilla->login(); } my $user = Bugzilla->user; diff --git a/queryhelp.cgi b/queryhelp.cgi index 60ebdd120..361ebc382 100755 --- a/queryhelp.cgi +++ b/queryhelp.cgi @@ -28,8 +28,7 @@ use lib qw(.); require "CGI.pl"; -ConnectToDatabase(); -quietly_check_login(); +Bugzilla->login(); GetVersionTable(); diff --git a/quips.cgi b/quips.cgi index bf1c41450..9bb6ea43a 100755 --- a/quips.cgi +++ b/quips.cgi @@ -36,8 +36,9 @@ use lib qw(.); require "CGI.pl"; -ConnectToDatabase(); -confirm_login(); +use Bugzilla::Constants; + +Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; diff --git a/relogin.cgi b/relogin.cgi index b7ba4f61e..6843405c2 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -30,11 +30,9 @@ use lib qw(.); require "CGI.pl"; # We don't want to remove a random logincookie from the db, so -# call quietly_check_login. If we're logged in after this, then +# call Bugzilla->login(). If we're logged in after this, then # the logincookie must be correct - -ConnectToDatabase(); -quietly_check_login(); +Bugzilla->login(); Bugzilla->logout(); diff --git a/report.cgi b/report.cgi index 2c0c430a9..cb872fc5b 100755 --- a/report.cgi +++ b/report.cgi @@ -29,6 +29,7 @@ require "CGI.pl"; use vars qw($template $vars); use Bugzilla; +use Bugzilla::Constants; my $cgi = Bugzilla->cgi; @@ -44,11 +45,9 @@ if (grep(/^cmd-/, $cgi->param())) { use Bugzilla::Search; -ConnectToDatabase(); - GetVersionTable(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); Bugzilla->switch_to_shadow_db(); diff --git a/reports.cgi b/reports.cgi index b863249d6..01ce99277 100755 --- a/reports.cgi +++ b/reports.cgi @@ -56,8 +56,7 @@ use Bugzilla; # If we're using bug groups for products, we should apply those restrictions # to viewing reports, as well. Time to check the login in that case. -ConnectToDatabase(); -quietly_check_login(); +Bugzilla->login(); GetVersionTable(); diff --git a/request.cgi b/request.cgi index 90304a2b9..43be69856 100755 --- a/request.cgi +++ b/request.cgi @@ -31,9 +31,6 @@ use strict; use lib qw(.); require "CGI.pl"; -# Establish a connection to the database backend. -ConnectToDatabase(); - # Use Bugzilla's Request module which contains utilities for handling requests. use Bugzilla::Flag; use Bugzilla::FlagType; @@ -44,7 +41,7 @@ use Bugzilla::User; use vars qw($template $vars @legal_product @legal_components %components); # Make sure the user is logged in. -quietly_check_login(); +Bugzilla->login(); ################################################################################ # Main Body Execution diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 8060c1c99..51b499025 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -70,9 +70,7 @@ sub BugListLinks { # Start ########################################################################### -ConnectToDatabase(); - -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; diff --git a/show_activity.cgi b/show_activity.cgi index e1697255b..5ab4e366e 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -30,14 +30,12 @@ use vars qw ($template $vars); require "CGI.pl"; my $cgi = Bugzilla->cgi; -ConnectToDatabase(); - ############################################################################### # Begin Data/Security Validation ############################################################################### # Check whether or not the user is currently logged in. -quietly_check_login(); +Bugzilla->login(); # Make sure the bug ID is a positive integer representing an existing # bug that the user is authorized to access. diff --git a/show_bug.cgi b/show_bug.cgi index 6d971bbfc..4b2459f90 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -25,11 +25,10 @@ use strict; use lib qw(.); use Bugzilla; +use Bugzilla::Constants; require "CGI.pl"; -ConnectToDatabase(); - use vars qw($template $vars $userid); use Bugzilla::Bug; @@ -37,9 +36,9 @@ use Bugzilla::Bug; my $cgi = Bugzilla->cgi; if ($::FORM{'GoAheadAndLogIn'}) { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); } else { - quietly_check_login(); + Bugzilla->login(); } # Editable, 'single' HTML bugs are treated slightly specially in a few places diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index a863df142..e0bd376d5 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -31,9 +31,7 @@ use Bugzilla::Config qw(:DEFAULT $webdotdir); require "CGI.pl"; -ConnectToDatabase(); - -quietly_check_login(); +Bugzilla->login(); my $cgi = Bugzilla->cgi; diff --git a/showdependencytree.cgi b/showdependencytree.cgi index b82443226..202043acd 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -31,9 +31,7 @@ require "CGI.pl"; # Use global template variables. use vars qw($template $vars); -ConnectToDatabase(); - -quietly_check_login(); +Bugzilla->login(); my $cgi = Bugzilla->cgi; diff --git a/sidebar.cgi b/sidebar.cgi index cf801eba3..73a22d1b3 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -26,8 +26,7 @@ use vars qw( $vars ); -ConnectToDatabase(); -quietly_check_login(); +Bugzilla->login(); my $cgi = Bugzilla->cgi; diff --git a/token.cgi b/token.cgi index 697da39b1..36508f0a5 100755 --- a/token.cgi +++ b/token.cgi @@ -32,14 +32,14 @@ use lib qw(.); use vars qw($template $vars); use Bugzilla; +use Bugzilla::Constants; + my $cgi = Bugzilla->cgi; # Include the Bugzilla CGI and general utility library. require "CGI.pl"; -# Establish a connection to the database backend. -ConnectToDatabase(); -quietly_check_login('permit_anonymous'); +Bugzilla->login(LOGIN_OPTIONAL); # Use the "Bugzilla::Token" module that contains functions for doing various # token-related tasks. diff --git a/userprefs.cgi b/userprefs.cgi index 15afdb21c..eefe40205 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -308,8 +308,7 @@ sub DoSavedSearches() { # Live code (not subroutine definitions) starts here ############################################################################### -ConnectToDatabase(); -confirm_login(); +Bugzilla->login(LOGIN_REQUIRED); GetVersionTable(); diff --git a/votes.cgi b/votes.cgi index ed7f6ad51..937149b3d 100755 --- a/votes.cgi +++ b/votes.cgi @@ -27,14 +27,13 @@ use strict; use lib "."; use Bugzilla; +use Bugzilla::Constants; require "CGI.pl"; # Use global template variables use vars qw($template $vars); -ConnectToDatabase(); - my $cgi = Bugzilla->cgi; # If the action is show_bug, you need a bug_id. @@ -51,10 +50,10 @@ my $action = $::FORM{'action'} || if ($action eq "show_bug" || ($action eq "show_user" && defined($::FORM{'user'}))) { - quietly_check_login(); + Bugzilla->login(); } else { - confirm_login(); + Bugzilla->login(LOGIN_REQUIRED); } ################################################################################ diff --git a/whineatnews.pl b/whineatnews.pl index 57a823583..b5fd020a9 100755 --- a/whineatnews.pl +++ b/whineatnews.pl @@ -31,8 +31,6 @@ use strict; require "globals.pl"; -ConnectToDatabase(); - SendSQL("select bug_id,short_desc,login_name from bugs,profiles where " . "(bug_status = 'NEW' or bug_status = 'REOPENED') and " . "to_days(now()) - to_days(delta_ts) > " . Param('whinedays') . -- cgit v1.2.3-65-gdbad From b9c3dbad825ee0936d1687aff9fdf122d88d6dac Mon Sep 17 00:00:00 2001 From: "jocuri%softhome.net" <> Date: Sat, 10 Apr 2004 22:08:21 +0000 Subject: Patch for bug 87770: make attachment.cgi work with no parameters; patch by GavinS ; r=kiko; a=myk. --- attachment.cgi | 31 +++++++++++++++--- show_bug.cgi | 2 +- template/en/default/attachment/choose.html.tmpl | 43 +++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 template/en/default/attachment/choose.html.tmpl (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 61565f01f..d851e537e 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -68,17 +68,27 @@ my $cgi = Bugzilla->cgi; # Main Body Execution ################################################################################ -# All calls to this script should contain an "action" variable whose value -# determines what the user wants to do. The code below checks the value of -# that variable and runs the appropriate code. +# All calls to this script should contain an "action" variable whose +# value determines what the user wants to do. The code below checks +# the value of that variable and runs the appropriate code. If none is +# supplied, we default to 'view'. # Determine whether to use the action specified by the user or the default. my $action = $::FORM{'action'} || 'view'; +# Slight awkward extra checks for the case when we came here from the +# attachment/choose.html.tmpl page +if ($action eq 'View') { + $action = 'view'; +} +elsif ($action eq 'Edit') { + $action = 'edit'; +} + if ($action eq "view") -{ +{ validateID(); - view(); + view(); } elsif ($action eq "interdiff") { @@ -165,6 +175,17 @@ sub validateID { my $param = @_ ? $_[0] : 'id'; + # Only do this check for no 'id' parameter if we are trying to + # validate the 'id' parameter + if ($param eq 'id' && !$cgi->param('id')) { + + print Bugzilla->cgi->header(); + $template->process("attachment/choose.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + exit; + + } + # Validate the value of the "id" form field, which must contain an # integer that is the ID of an existing attachment. diff --git a/show_bug.cgi b/show_bug.cgi index 9093550ef..7a568e3f5 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -46,7 +46,7 @@ my $single = !$cgi->param('format') && (!$cgi->param('ctype') || $cgi->param('ctype') eq 'html'); # If we don't have an ID, _AND_ we're only doing a single bug, then prompt -if (!defined $cgi->param('id') && $single) { +if (!$cgi->param('id') && $single) { print Bugzilla->cgi->header(); $template->process("bug/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/template/en/default/attachment/choose.html.tmpl b/template/en/default/attachment/choose.html.tmpl new file mode 100644 index 000000000..b48664653 --- /dev/null +++ b/template/en/default/attachment/choose.html.tmpl @@ -0,0 +1,43 @@ +[%# 1.0@bugzilla.org %] +[%# 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): Gavin Shelley + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% PROCESS global/header.html.tmpl + title = "Locate attachment" + %] + + +

Access an attachment by entering its ID into the form below:

+

Attachment ID: + + +

+ + +
+

Or, access it from the list of attachments in its associated [% terms.bug %] report:

+

[% terms.Bug %] ID: + +

+
+ +[% PROCESS global/footer.html.tmpl %] -- cgit v1.2.3-65-gdbad From 6d413a7ccc12dcd446f15dbee9a051f52b85f270 Mon Sep 17 00:00:00 2001 From: "kiko%async.com.br" <> Date: Wed, 14 Apr 2004 03:31:26 +0000 Subject: Additional fix for bug 87770: attachment.cgi should work with no parameters. Use +

-- cgit v1.2.3-65-gdbad From 8def33fe42d16d86d9cb660681af677b3b9db219 Mon Sep 17 00:00:00 2001 From: "justdave%syndicomm.com" <> Date: Tue, 27 Apr 2004 10:58:55 +0000 Subject: Bug 234540: "Take bug" on create attachment screen missed an API change to BugMail which caused it not to mail the previous bug owner about the change. r=gerv, a=justdave --- attachment.cgi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 67e8559c4..44a49c5f7 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -885,7 +885,7 @@ sub insert } # Assign the bug to the user, if they are allowed to take it - my $forcecc = ""; + my $owner = ""; if ($::FORM{'takebug'} && UserInGroup("editbugs")) { SendSQL("select NOW()"); @@ -902,7 +902,7 @@ sub insert my @newvalues = ($::userid, "ASSIGNED", "", DBID_to_name($::userid)); # Make sure the person we are taking the bug from gets mail. - $forcecc = $oldvalues[3]; + $owner = $oldvalues[3]; @oldvalues = map(SqlQuote($_), @oldvalues); @newvalues = map(SqlQuote($_), @newvalues); @@ -930,7 +930,8 @@ sub insert } # Define the variables and functions that will be passed to the UI template. - $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} }; + $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'}, + 'owner' => $owner }; $vars->{'bugid'} = $::FORM{'bugid'}; $vars->{'attachid'} = $attachid; $vars->{'description'} = $description; -- cgit v1.2.3-65-gdbad From 8b0e08269dce8c37b35b0433c5ff2976c6a04214 Mon Sep 17 00:00:00 2001 From: "justdave%bugzilla.org" <> Date: Mon, 10 May 2004 23:57:11 +0000 Subject: Bug 204042: taint issues in perl 5.6.0 that were causing an Internal Error to ocurr after adding an attachment. r= joel, a= justdave --- attachment.cgi | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 44a49c5f7..83a910ee0 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -932,7 +932,13 @@ sub insert # Define the variables and functions that will be passed to the UI template. $vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'}, 'owner' => $owner }; - $vars->{'bugid'} = $::FORM{'bugid'}; + my $bugid = $::FORM{'bugid'}; + detaint_natural($bugid); # don't bother with error condition, we know it'll work + # because of ValidateBugID above. This is only needed + # for Perl 5.6.0. If we ever require Perl 5.6.1 or + # newer, or detaint something other than $::FORM{'bugid'} + # in ValidateBugID above, then this can go away. + $vars->{'bugid'} = $bugid; $vars->{'attachid'} = $attachid; $vars->{'description'} = $description; $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; -- cgit v1.2.3-65-gdbad From 90f6e932e23ec82dd31149a118661a91f51a5e90 Mon Sep 17 00:00:00 2001 From: "jouni%heikniemi.net" <> Date: Sun, 30 May 2004 22:52:12 +0000 Subject: Bug 223541: Make flags show up correctly in "View all attachments" mode. r=joel a=justdave --- attachment.cgi | 1 + template/en/default/attachment/show-multiple.html.tmpl | 15 +++++++++++---- template/en/default/filterexceptions.pl | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 83a910ee0..c1e8f9dd0 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -768,6 +768,7 @@ sub viewall $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $a{'datasize'}) = FetchSQLData(); $a{'isviewable'} = isViewable($a{'contenttype'}); + $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'} }); # Add the hash representing the attachment to the array of attachments. push @attachments, \%a; diff --git a/template/en/default/attachment/show-multiple.html.tmpl b/template/en/default/attachment/show-multiple.html.tmpl index bcfae488d..e7043a142 100644 --- a/template/en/default/attachment/show-multiple.html.tmpl +++ b/template/en/default/attachment/show-multiple.html.tmpl @@ -61,11 +61,18 @@
[% a.datasize FILTER unitconvert %] - [% IF a.statuses.size == 0 %] - none + [% IF a.flags.size == 0 %] + no flags [% ELSE %] - [% FOREACH s = a.statuses %] - [% s FILTER html FILTER replace('\s', ' ') %]
+ [% FOREACH flag = a.flags %] + [% IF flag.setter %] + [% flag.setter.nick FILTER html %]: + [% END %] + [%+ flag.type.name FILTER html %][% flag.status %] + [% IF flag.status == "?" && flag.requestee %] + ([% flag.requestee.nick FILTER html %]) + [% END %] + [% ", " IF !loop.last %] [% END %] [% END %]
+ [% IF flag_types.size > 0 %] + [% PROCESS "flag/list.html.tmpl" bug_id=bugid attach_id=attachid %]
+ [% END %] +
Comment: -- cgit v1.2.3-65-gdbad From 3f73d7e93eee2578d7331407c62b19a396e64486 Mon Sep 17 00:00:00 2001 From: "kiko%async.com.br" <> Date: Sun, 31 Oct 2004 07:50:28 +0000 Subject: Comment-only fix: be honest about where we put that string! r=ssdbot --- attachment.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 96a8ba6f8..31dcd12e7 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1160,7 +1160,7 @@ sub update $Text::Wrap::columns = 80; $Text::Wrap::huge = 'wrap'; - # Append a string to the comment to let users know that the comment came from + # Prepend a string to the comment to let users know that the comment came from # the "edit attachment" screen. my $comment = qq|(From update of attachment $::FORM{'id'})\n| . $::FORM{'comment'}; -- cgit v1.2.3-65-gdbad From c068e2b90f06f898217dc9919371a583517d985e Mon Sep 17 00:00:00 2001 From: "jake%bugzilla.org" <> Date: Tue, 21 Dec 2004 17:04:00 +0000 Subject: Bug 264601 - Bugzilla will now tell a user which field contained an "invalid bug number or alias." Patch by Frédéric Buclin r=myk, a=justdave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CGI.pl | 8 ++-- attachment.cgi | 5 --- post_bug.cgi | 2 +- process_bug.cgi | 52 ++++++++---------------- template/en/default/global/field-descs.none.tmpl | 3 ++ template/en/default/global/user-error.html.tmpl | 28 ++++++------- 6 files changed, 39 insertions(+), 59 deletions(-) (limited to 'attachment.cgi') diff --git a/CGI.pl b/CGI.pl index 456022808..0d03562fe 100644 --- a/CGI.pl +++ b/CGI.pl @@ -148,7 +148,7 @@ sub ValidateBugID { # database, and that the user is authorized to access that bug. # We detaint the number here, too - my ($id, $skip_authorization) = @_; + my ($id, $field) = @_; # Get rid of white-space around the ID. $id = trim($id); @@ -157,7 +157,9 @@ sub ValidateBugID { my $alias = $id; if (!detaint_natural($id)) { $id = BugAliasToID($alias); - $id || ThrowUserError("invalid_bug_id_or_alias", {'bug_id' => $alias}); + $id || ThrowUserError("invalid_bug_id_or_alias", + {'bug_id' => $alias, + 'field' => $field }); } # Modify the calling code's original variable to contain the trimmed, @@ -170,7 +172,7 @@ sub ValidateBugID { FetchOneColumn() || ThrowUserError("invalid_bug_id_non_existent", {'bug_id' => $id}); - return if $skip_authorization; + return if ($field eq "dependson" || $field eq "blocked"); return if Bugzilla->user->can_see_bug($id); diff --git a/attachment.cgi b/attachment.cgi index 31dcd12e7..b486bf6db 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1060,11 +1060,6 @@ sub update # Get the bug ID for the bug to which this attachment is attached. SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); my $bugid = FetchSQLData(); - unless ($bugid) - { - ThrowUserError("invalid_bug_id", - { bug_id => $bugid }); - } # Lock database tables in preparation for updating the attachment. SendSQL("LOCK TABLES attachments WRITE , flags WRITE , " . diff --git a/post_bug.cgi b/post_bug.cgi index 5fc50e66f..7282f8fa9 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -256,7 +256,7 @@ foreach my $field ("dependson", "blocked") { my @validvalues; foreach my $id (split(/[\s,]+/, $::FORM{$field})) { next unless $id; - ValidateBugID($id, 1); + ValidateBugID($id, $field); push(@validvalues, $id); } $::FORM{$field} = join(",", @validvalues); diff --git a/process_bug.cgi b/process_bug.cgi index 47957d193..6fb94711a 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -120,23 +120,13 @@ foreach my $field ("dependson", "blocked") { my @validvalues; foreach my $id (split(/[\s,]+/, $::FORM{$field})) { next unless $id; - ValidateBugID($id, 1); + ValidateBugID($id, $field); push(@validvalues, $id); } $::FORM{$field} = join(",", @validvalues); } } -# If we are duping bugs, let's also make sure that we can change -# the original. This takes care of issue A on bug 96085. -if (defined $::FORM{'dup_id'} && $::FORM{'knob'} eq "duplicate") { - ValidateBugID($::FORM{'dup_id'}); - - # Also, let's see if the reporter has authorization to see the bug - # to which we are duping. If not we need to prompt. - DuplicateUserConfirm(); -} - # do a match on the fields if applicable # The order of these function calls is important, as both Flag::validate @@ -490,8 +480,8 @@ sub DuplicateUserConfirm { return; } - my $dupe = trim($::FORM{'id'}); - my $original = trim($::FORM{'dup_id'}); + my $dupe = $::FORM{'id'}; + my $original = $::FORM{'dup_id'}; SendSQL("SELECT reporter FROM bugs WHERE bug_id = " . SqlQuote($dupe)); my $reporter = FetchOneColumn(); @@ -520,7 +510,7 @@ sub DuplicateUserConfirm { $template->process("bug/process/confirm-duplicate.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; -} # end DuplicateUserConfirm() +} if (defined $::FORM{'id'}) { # since this means that we were called from show_bug.cgi, now is a good @@ -976,28 +966,22 @@ SWITCH: for ($::FORM{'knob'}) { last SWITCH; }; /^duplicate$/ && CheckonComment( "duplicate" ) && do { - ChangeStatus('RESOLVED'); - ChangeResolution('DUPLICATE'); - CheckFormFieldDefined(\%::FORM,'dup_id'); - my $num = trim($::FORM{'dup_id'}); - SendSQL("SELECT bug_id FROM bugs WHERE bug_id = " . SqlQuote($num)); - $num = FetchOneColumn(); - if (!$num) { - ThrowUserError("dupe_invalid_bug_id") - } - if (!defined($::FORM{'id'}) || $num == $::FORM{'id'}) { + # Make sure we can change the original bug (issue A on bug 96085) + CheckFormFieldDefined(\%::FORM, 'dup_id'); + ValidateBugID($::FORM{'dup_id'}, 'dup_id'); + + # Also, let's see if the reporter has authorization to see + # the bug to which we are duping. If not we need to prompt. + DuplicateUserConfirm(); + + $duplicate = $::FORM{'dup_id'}; + if (!defined($::FORM{'id'}) || $duplicate == $::FORM{'id'}) { ThrowUserError("dupe_of_self_disallowed"); } - my $checkid = trim($::FORM{'id'}); - SendSQL("SELECT bug_id FROM bugs where bug_id = " . SqlQuote($checkid)); - $checkid = FetchOneColumn(); - if (!$checkid) { - ThrowUserError("invalid_bug_id", - { bug_id => $checkid }); - } - $::FORM{'comment'} .= "\n\n*** This bug has been marked as a duplicate of $num ***"; - $duplicate = $num; - + ChangeStatus('RESOLVED'); + ChangeResolution('DUPLICATE'); + $::FORM{'comment'} .= "\n\n*** This bug has been marked " . + "as a duplicate of $duplicate ***"; last SWITCH; }; diff --git a/template/en/default/global/field-descs.none.tmpl b/template/en/default/global/field-descs.none.tmpl index 1080e8e44..e49db6b5b 100644 --- a/template/en/default/global/field-descs.none.tmpl +++ b/template/en/default/global/field-descs.none.tmpl @@ -26,6 +26,7 @@ [% field_descs = { "[Bug creation]" => "[$terms.Bug creation]", "alias" => "Alias", "assigned_to" => "Assignee", + "blocked" => "Blocks", "bug_file_loc" => "URL", "bug_id" => "$terms.Bug ID", "bug_severity" => "Severity", @@ -38,6 +39,8 @@ "component" => "Component", "creation_ts" => "$terms.Bug Creation time", "delta_ts" => "Last Changed time", + "dependson" => "Depends on", + "dup_id" => "Duplicate", "estimated_time" => "Orig. Est.", "everconfirmed" => "Ever confirmed?", "groupset" => "Groupset", diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index affad39cc..ebd44906e 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -262,11 +262,6 @@ [% title = "Description Required" %] You must provide a description of the [% terms.bug %]. - [% ELSIF error == "dupe_invalid_bug_id" %] - [% title = BLOCK %]Valid [% terms.Bug %] Number Required[% END %] - You must specify a valid [% terms.bug %] number of which this - [%+ terms.bug %] is a duplicate. The [% terms.bug %] has not been changed. - [% ELSIF error == "dupe_of_self_disallowed" %] [% title = "Cannot mark $terms.abug as a duplicate of itself" %] You can't mark [% terms.abug %] as a duplicate of itself. @@ -468,19 +463,24 @@ [% title = "Invalid Attachment ID" %] The attachment id [% attach_id FILTER html %] is invalid. - [% ELSIF error == "invalid_bug_id" %] - [% title = BLOCK %]Invalid [% terms.Bug %] ID[% END %] - The [% terms.bug %] id [% bug_id FILTER html %] is invalid. - [% ELSIF error == "invalid_bug_id_non_existent" %] [% title = BLOCK %]Invalid [% terms.Bug %] ID[% END %] [% terms.Bug %] #[% bug_id FILTER html %] does not exist. [% ELSIF error == "invalid_bug_id_or_alias" %] [% title = BLOCK %]Invalid [% terms.Bug %] ID[% END %] - '[% bug_id FILTER html %]' is not a valid [% terms.bug %] number - [% IF Param("usebugaliases") %] nor an alias - to [% terms.abug %] number[% END %]. + [% IF bug_id %] + '[% bug_id FILTER html %]' is not a valid [% terms.bug %] number + [% IF Param("usebugaliases") %] + nor an alias to [% terms.abug %] number + [% END %]. + [% ELSE %] + [% IF field %] + The '[% field_descs.$field FILTER html %]' field + cannot be empty. + [% END %] + You must enter a valid [% terms.bug %] number! + [% END %] - +

Expand on the Summary. Please be as specific as possible about what is wrong. @@ -372,7 +371,7 @@ function PutDescription() { Steps to Reproduce

- +

What happened after you performed the steps above?

@@ -402,8 +400,7 @@ function PutDescription() { Expected Results
- +

What should the software have done instead?

@@ -415,8 +412,7 @@ function PutDescription() { Additional Information
- +

Add any additional information you feel may be relevant to this [% terms.bug %], such as the theme you were diff --git a/template/en/default/bug/create/create.html.tmpl b/template/en/default/bug/create/create.html.tmpl index a5d5e213c..013a05f24 100644 --- a/template/en/default/bug/create/create.html.tmpl +++ b/template/en/default/bug/create/create.html.tmpl @@ -251,7 +251,7 @@ function set_assign_to() {

Description: - [% IF NOT bug.cc || NOT bug.cc.contains(user.login) %] diff --git a/template/en/default/list/edit-multiple.html.tmpl b/template/en/default/list/edit-multiple.html.tmpl index b705ba574..c2c8bf4d8 100644 --- a/template/en/default/list/edit-multiple.html.tmpl +++ b/template/en/default/list/edit-multiple.html.tmpl @@ -186,7 +186,7 @@

-
+
[% IF groups.size > 0 %] diff --git a/template/en/default/pages/linked.html.tmpl b/template/en/default/pages/linked.html.tmpl index fcb5ee9d0..bd5933094 100644 --- a/template/en/default/pages/linked.html.tmpl +++ b/template/en/default/pages/linked.html.tmpl @@ -32,7 +32,7 @@

-[%- cgi.param("text") FILTER quoteUrls FILTER html -%]
+[%- cgi.param("text") FILTER wrap_comment FILTER quoteUrls FILTER html -%]
 

@@ -47,7 +47,7 @@

-[%- cgi.param("text") FILTER quoteUrls -%]
+[%- cgi.param("text") FILTER wrap_comment FILTER quoteUrls -%]
 

diff --git a/template/en/default/pages/linkify.html.tmpl b/template/en/default/pages/linkify.html.tmpl index 40cda81cb..896f7d41f 100644 --- a/template/en/default/pages/linkify.html.tmpl +++ b/template/en/default/pages/linkify.html.tmpl @@ -30,7 +30,7 @@

- +
-- cgit v1.2.3-65-gdbad From d3f8bf365e5b93f58497a25e07fde7ce30884f9d Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Fri, 18 Feb 2005 05:57:26 +0000 Subject: Bug 280503: Replace "LOCK/UNLOCK TABLES" with Bugzilla::DB function call Patch By Tomas Kopal r=mkanat,a=myk --- Bugzilla/Constants.pm | 6 +++++ Bugzilla/DB.pm | 4 +-- Bugzilla/Error.pm | 3 ++- Bugzilla/Series.pm | 4 +-- Bugzilla/Token.pm | 22 +++++++++++------ Bugzilla/User.pm | 9 +++---- attachment.cgi | 21 ++++++++-------- buglist.cgi | 4 +-- checksetup.pl | 8 +++--- editclassifications.cgi | 12 ++++----- editcomponents.cgi | 32 ++++++++++++------------ editflagtypes.cgi | 48 +++++++++++++++++++++--------------- editgroups.cgi | 16 ++++++------ editmilestones.cgi | 26 ++++++++++---------- editproducts.cgi | 65 ++++++++++++++++++++++++++----------------------- editversions.cgi | 25 ++++++++++--------- process_bug.cgi | 27 ++++++++++---------- query.cgi | 6 ++--- sanitycheck.cgi | 14 ++++++----- token.cgi | 21 ++++++++++------ userprefs.cgi | 7 +++--- votes.cgi | 31 ++++++++++++----------- whine.pl | 13 +++++----- 23 files changed, 232 insertions(+), 192 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 3ef3cc634..a3e16251c 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -66,6 +66,8 @@ use base qw(Exporter); DEFAULT_QUERY_NAME COMMENT_COLS + + UNLOCK_ABORT ); @Bugzilla::Constants::EXPORT_OK = qw(contenttypes); @@ -217,4 +219,8 @@ use constant DEFAULT_QUERY_NAME => '(Default query)'; # The column length for displayed (and wrapped) bug comments. use constant COMMENT_COLS => 80; +# used by Bugzilla::DB to indicate that tables are being unlocked +# because of error +use constant UNLOCK_ABORT => 1; + 1; diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 3e3f45c66..d1ecfcb2e 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -501,8 +501,8 @@ formatted SQL command have prefix C. All other methods have prefix C. set even without locking tables first without raising an error to simplify error handling. Abstract method, should be overriden by database specific code. - Params: $abort = true (1) if the operation on locked tables failed - (if transactions are supported, the action will be rolled + Params: $abort = UNLOCK_ABORT (true, 1) if the operation on locked tables + failed (if transactions are supported, the action will be rolled back). False (0) or no param if the operation succeeded. Returns: none diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index 96017f368..e86b1c41a 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -27,6 +27,7 @@ use base qw(Exporter); @Bugzilla::Error::EXPORT = qw(ThrowCodeError ThrowTemplateError ThrowUserError); use Bugzilla::Config; +use Bugzilla::Constants; use Bugzilla::Util; use Date::Format; @@ -37,7 +38,7 @@ sub _throw_error { $vars->{error} = $error; - Bugzilla->dbh->do("UNLOCK TABLES") if $unlock_tables; + Bugzilla->dbh->bz_unlock_tables(UNLOCK_ABORT) if $unlock_tables; # If a writable data/errorlog exists, log error details there. if (-w "data/errorlog") { diff --git a/Bugzilla/Series.pm b/Bugzilla/Series.pm index a4bd6654f..53e6fbabf 100644 --- a/Bugzilla/Series.pm +++ b/Bugzilla/Series.pm @@ -170,7 +170,7 @@ sub writeToDatabase { my $self = shift; my $dbh = Bugzilla->dbh; - $dbh->do("LOCK TABLES series_categories WRITE, series WRITE"); + $dbh->bz_lock_tables('series_categories WRITE', 'series WRITE'); my $category_id = getCategoryID($self->{'category'}); my $subcategory_id = getCategoryID($self->{'subcategory'}); @@ -210,7 +210,7 @@ sub writeToDatabase { || &::ThrowCodeError("missing_series_id", { 'series' => $self }); } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } # Check whether a series with this name, category and subcategory exists in diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 90efe99bd..9caf91ab2 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -52,13 +52,14 @@ my $maxtokenage = 3; sub IssueEmailChangeToken { my ($userid, $old_email, $new_email) = @_; + my $dbh = Bugzilla->dbh; my $token_ts = time(); my $issuedate = time2str("%Y-%m-%d %H:%M", $token_ts); # Generate a unique token and insert it into the tokens table. # We have to lock the tokens table before generating the token, # since the database must be queried for token uniqueness. - &::SendSQL("LOCK TABLES tokens WRITE"); + $dbh->bz_lock_tables('tokens WRITE'); my $token = GenerateUniqueToken(); my $quotedtoken = &::SqlQuote($token); my $quoted_emails = &::SqlQuote($old_email . ":" . $new_email); @@ -72,7 +73,7 @@ sub IssueEmailChangeToken { tokentype , eventdata ) VALUES ( $userid , '$issuedate' , $quotedtoken , 'emailnew' , $quoted_emails )"); - &::SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # Mail the user the token along with instructions for using it. @@ -110,6 +111,8 @@ sub IssuePasswordToken { my ($loginname) = @_; + my $dbh = Bugzilla->dbh; + # Retrieve the user's ID from the database. my $quotedloginname = &::SqlQuote($loginname); &::SendSQL("SELECT profiles.userid, tokens.issuedate FROM profiles @@ -129,13 +132,13 @@ sub IssuePasswordToken { # Generate a unique token and insert it into the tokens table. # We have to lock the tokens table before generating the token, # since the database must be queried for token uniqueness. - &::SendSQL("LOCK TABLES tokens WRITE"); + $dbh->bz_lock_tables('tokens WRITE'); my $token = GenerateUniqueToken(); my $quotedtoken = &::SqlQuote($token); my $quotedipaddr = &::SqlQuote($::ENV{'REMOTE_ADDR'}); &::SendSQL("INSERT INTO tokens ( userid , issuedate , token , tokentype , eventdata ) VALUES ( $userid , NOW() , $quotedtoken , 'password' , $quotedipaddr )"); - &::SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # Mail the user the token along with instructions for using it. @@ -158,10 +161,11 @@ sub IssuePasswordToken { sub CleanTokenTable { - &::SendSQL("LOCK TABLES tokens WRITE"); + my $dbh = Bugzilla->dbh; + $dbh->bz_lock_tables('tokens WRITE'); &::SendSQL("DELETE FROM tokens WHERE TO_DAYS(NOW()) - TO_DAYS(issuedate) >= " . $maxtokenage); - &::SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } @@ -198,6 +202,8 @@ sub Cancel { my ($token, $cancelaction) = @_; + my $dbh = Bugzilla->dbh; + # Quote the token for inclusion in SQL statements. my $quotedtoken = &::SqlQuote($token); @@ -232,9 +238,9 @@ sub Cancel { Bugzilla::BugMail::MessageToMTA($message); # Delete the token from the database. - &::SendSQL("LOCK TABLES tokens WRITE"); + $dbh->bz_lock_tables('tokens WRITE'); &::SendSQL("DELETE FROM tokens WHERE token = $quotedtoken"); - &::SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } sub DeletePasswordTokens { diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 8f5f6a762..67b79f168 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -384,10 +384,9 @@ sub derive_groups { my $sth; - $dbh->do(q{LOCK TABLES profiles WRITE, - user_group_map WRITE, - group_group_map READ, - groups READ}) unless $already_locked; + $dbh->bz_lock_tables('profiles WRITE', 'user_group_map WRITE', + 'group_group_map READ', + 'groups READ') unless $already_locked; # avoid races, we are only up to date as of the BEGINNING of this process my $time = $dbh->selectrow_array("SELECT NOW()"); @@ -459,7 +458,7 @@ sub derive_groups { undef, $time, $id); - $dbh->do("UNLOCK TABLES") unless $already_locked; + $dbh->bz_unlock_tables() unless $already_locked; } sub can_bless { diff --git a/attachment.cgi b/attachment.cgi index d58395efc..5e10d8fee 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1051,15 +1051,16 @@ sub edit sub update { # Updates an attachment record. + my $dbh = Bugzilla->dbh; # Get the bug ID for the bug to which this attachment is attached. SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); my $bugid = FetchSQLData(); - + # Lock database tables in preparation for updating the attachment. - SendSQL("LOCK TABLES attachments WRITE , flags WRITE , " . - "flagtypes READ , fielddefs READ , bugs_activity WRITE, " . - "flaginclusions AS i READ, flagexclusions AS e READ, " . + $dbh->bz_lock_tables('attachments WRITE', 'flags WRITE' , + 'flagtypes READ', 'fielddefs READ', 'bugs_activity WRITE', + 'flaginclusions AS i READ', 'flagexclusions AS e READ', # cc, bug_group_map, user_group_map, and groups are in here so we # can check the permissions of flag requestees and email addresses # on the flag type cc: lists via the CanSeeBug @@ -1067,10 +1068,10 @@ sub update # Bugzilla::User needs to rederive groups. profiles and # user_group_map would be READ locks instead of WRITE locks if it # weren't for derive_groups, which needs to write to those tables. - "bugs READ, profiles WRITE, " . - "cc READ, bug_group_map READ, user_group_map WRITE, " . - "group_group_map READ, groups READ"); - + 'bugs READ', 'profiles WRITE', + 'cc READ', 'bug_group_map READ', 'user_group_map WRITE', + 'group_group_map READ', 'groups READ'); + # Get a copy of the attachment record before we make changes # so we can record those changes in the activity table. SendSQL("SELECT description, mimetype, filename, ispatch, isobsolete, isprivate @@ -1138,9 +1139,9 @@ sub update # Update flags. my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); Bugzilla::Flag::process($target, $timestamp, \%::FORM); - + # Unlock all database tables now that we are finished updating the database. - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # If the user submitted a comment while editing the attachment, # add the comment to the bug. diff --git a/buglist.cgi b/buglist.cgi index f6869b349..891926d4e 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -250,7 +250,7 @@ sub InsertNamedQuery ($$$;$) { # it when we display it to the user. trick_taint($query); - $dbh->do("LOCK TABLES namedqueries WRITE"); + $dbh->bz_lock_tables('namedqueries WRITE'); my $result = $dbh->selectrow_array("SELECT userid FROM namedqueries" . " WHERE userid = ? AND name = ?" @@ -269,7 +269,7 @@ sub InsertNamedQuery ($$$;$) { , undef, ($userid, $query_name, $query, $link_in_footer)); } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); return $query_existed_before; } diff --git a/checksetup.pl b/checksetup.pl index c99beb3a5..79095ee3a 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -2722,7 +2722,7 @@ if (GetFieldDef('bugs', 'long_desc')) { print "bugs to process; a line of dots will be printed for each 50.\n\n"; $| = 1; - $dbh->do("LOCK TABLES bugs write, longdescs write, profiles write"); + $dbh->bz_lock_tables('bugs write', 'longdescs write', 'profiles write'); $dbh->do('DELETE FROM longdescs'); @@ -2823,7 +2823,7 @@ if (GetFieldDef('bugs', 'long_desc')) { DropField('bugs', 'long_desc'); - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } @@ -2836,7 +2836,7 @@ if (GetFieldDef('bugs_activity', 'field')) { 'mediumint not null, ADD INDEX (fieldid)'); print "Populating new fieldid field ...\n"; - $dbh->do("LOCK TABLES bugs_activity WRITE, fielddefs WRITE"); + $dbh->bz_lock_tables('bugs_activity WRITE', 'fielddefs WRITE'); my $sth = $dbh->prepare('SELECT DISTINCT field FROM bugs_activity'); $sth->execute(); @@ -2856,7 +2856,7 @@ if (GetFieldDef('bugs_activity', 'field')) { } $dbh->do("UPDATE bugs_activity SET fieldid = $id WHERE field = $q"); } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); DropField('bugs_activity', 'field'); } diff --git a/editclassifications.cgi b/editclassifications.cgi index 777e76f75..fd02befef 100755 --- a/editclassifications.cgi +++ b/editclassifications.cgi @@ -205,7 +205,7 @@ if ($action eq 'delete') { } # lock the tables before we start to change everything: - $dbh->do("LOCK TABLES classifications WRITE, products WRITE"); + $dbh->bz_lock_tables('classifications WRITE', 'products WRITE'); # delete $sth = $dbh->prepare("DELETE FROM classifications WHERE id=?"); @@ -217,7 +217,7 @@ if ($action eq 'delete') { WHERE classification_id=?"); $sth->execute($classification_id); - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); unlink "data/versioncache"; @@ -283,7 +283,7 @@ if ($action eq 'update') { # above so it will remain static even after we rename the # classification in the database. - $dbh->do("LOCK TABLES classifications WRITE"); + $dbh->bz_lock_tables('classifications WRITE'); if ($description ne $descriptionold) { $sth = $dbh->prepare("UPDATE classifications @@ -295,12 +295,12 @@ if ($action eq 'update') { if ($classification ne $classificationold) { unless ($classification) { - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError("classification_not_specified") } if (TestClassification($classification)) { - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError("classification_already_exists", { name => $classification }); } $sth = $dbh->prepare("UPDATE classifications @@ -308,7 +308,7 @@ if ($action eq 'update') { $sth->execute($classification,$classification_id); $vars->{'updated_classification'} = 1; } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); unlink "data/versioncache"; LoadTemplate($action); diff --git a/editcomponents.cgi b/editcomponents.cgi index f1d20fbd5..08cfab14c 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -39,6 +39,7 @@ use Bugzilla::Util; use vars qw($template $vars); my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; my $showbugcounts = (defined $cgi->param('showbugcounts')); @@ -445,13 +446,13 @@ if ($action eq 'delete') { # lock the tables before we start to change everything: - SendSQL("LOCK TABLES attachments WRITE, - bugs WRITE, - bugs_activity WRITE, - components WRITE, - dependencies WRITE, - flaginclusions WRITE, - flagexclusions WRITE"); + $dbh->bz_lock_tables('attachments WRITE', + 'bugs WRITE', + 'bugs_activity WRITE', + 'components WRITE', + 'dependencies WRITE', + 'flaginclusions WRITE', + 'flagexclusions WRITE'); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries @@ -491,7 +492,7 @@ if ($action eq 'delete') { SendSQL("DELETE FROM components WHERE id=$component_id"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); unlink "$datadir/versioncache"; @@ -575,14 +576,15 @@ if ($action eq 'update') { # Note that the order of this tests is important. If you change # them, be sure to test for WHERE='$component' or WHERE='$componentold' - SendSQL("LOCK TABLES components WRITE, products READ, profiles READ"); + $dbh->bz_lock_tables('components WRITE', 'products READ', + 'profiles READ'); CheckComponent($product, $componentold); my $component_id = get_component_id(get_product_id($product), $componentold); if ($description ne $descriptionold) { unless ($description) { - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('component_blank_description', {'name' => $componentold}); exit; @@ -600,7 +602,7 @@ if ($action eq 'update') { my $initialownerid = DBname_to_id($initialowner); unless ($initialownerid) { - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('component_need_valid_initialowner', {'name' => $componentold}); exit; @@ -618,7 +620,7 @@ if ($action eq 'update') { if (Param('useqacontact') && $initialqacontact ne $initialqacontactold) { my $initialqacontactid = DBname_to_id($initialqacontact); if (!$initialqacontactid && $initialqacontact ne '') { - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('component_need_valid_initialqacontact', {'name' => $componentold}); exit; @@ -635,13 +637,13 @@ if ($action eq 'update') { if ($component ne $componentold) { unless ($component) { - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('component_must_have_a_name', {'name' => $componentold}); exit; } if (TestComponent($product, $component)) { - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('component_already_exists', {'name' => $component}); exit; @@ -655,7 +657,7 @@ if ($action eq 'update') { } - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'name'} = $component; $vars->{'product'} = $product; diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 48074863a..c28fda4ba 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -220,15 +220,18 @@ sub insert { validateIsRequesteeble(); validateAllowMultiple(); validateGroups(); - + + my $dbh = Bugzilla->dbh; + my $name = SqlQuote($::FORM{'name'}); my $description = SqlQuote($::FORM{'description'}); my $cc_list = SqlQuote($::FORM{'cc_list'}); my $target_type = $::FORM{'target_type'} eq "bug" ? "b" : "a"; - - SendSQL("LOCK TABLES flagtypes WRITE, products READ, components READ, " . - "flaginclusions WRITE, flagexclusions WRITE"); - + + $dbh->bz_lock_tables('flagtypes WRITE', 'products READ', + 'components READ', 'flaginclusions WRITE', + 'flagexclusions WRITE'); + # Determine the new flag type's unique identifier. SendSQL("SELECT MAX(id) FROM flagtypes"); my $id = FetchSQLData() + 1; @@ -255,8 +258,8 @@ sub insert { "component_id) VALUES ($id, $product_id, $component_id)"); } } - - SendSQL("UNLOCK TABLES"); + + $dbh->bz_unlock_tables(); $vars->{'name'} = $::FORM{'name'}; $vars->{'message'} = "flag_type_created"; @@ -282,13 +285,16 @@ sub update { validateIsRequesteeble(); validateAllowMultiple(); validateGroups(); - + + my $dbh = Bugzilla->dbh; + my $name = SqlQuote($::FORM{'name'}); my $description = SqlQuote($::FORM{'description'}); my $cc_list = SqlQuote($::FORM{'cc_list'}); - - SendSQL("LOCK TABLES flagtypes WRITE, products READ, components READ, " . - "flaginclusions WRITE, flagexclusions WRITE"); + + $dbh->bz_lock_tables('flagtypes WRITE', 'products READ', + 'components READ', 'flaginclusions WRITE', + 'flagexclusions WRITE'); SendSQL("UPDATE flagtypes SET name = $name , description = $description , @@ -316,7 +322,7 @@ sub update { } } - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # Clear existing flags for bugs/attachments in categories no longer on # the list of inclusions or that have been added to the list of exclusions. @@ -384,9 +390,11 @@ sub confirmDelete sub deleteType { validateID(); - - SendSQL("LOCK TABLES flagtypes WRITE, flags WRITE, " . - "flaginclusions WRITE, flagexclusions WRITE"); + + my $dbh = Bugzilla->dbh; + + $dbh->bz_lock_tables('flagtypes WRITE', 'flags WRITE', + 'flaginclusions WRITE', 'flagexclusions WRITE'); # Get the name of the flag type so we can tell users # what was deleted. @@ -397,7 +405,7 @@ sub deleteType { SendSQL("DELETE FROM flaginclusions WHERE type_id = $::FORM{'id'}"); SendSQL("DELETE FROM flagexclusions WHERE type_id = $::FORM{'id'}"); SendSQL("DELETE FROM flagtypes WHERE id = $::FORM{'id'}"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'message'} = "flag_type_deleted"; @@ -413,10 +421,12 @@ sub deleteType { sub deactivate { validateID(); validateIsActive(); - - SendSQL("LOCK TABLES flagtypes WRITE"); + + my $dbh = Bugzilla->dbh; + + $dbh->bz_lock_tables('flagtypes WRITE'); SendSQL("UPDATE flagtypes SET is_active = 0 WHERE id = $::FORM{'id'}"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'message'} = "flag_type_deactivated"; $vars->{'flag_type'} = Bugzilla::FlagType::get($::FORM{'id'}); diff --git a/editgroups.cgi b/editgroups.cgi index 818997114..c3be719c7 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -483,10 +483,8 @@ if (($action eq 'remove_all_regexp') || ($action eq 'remove_all')) { WHERE id = ?"); $sth->execute($gid); my ($name, $regexp) = $sth->fetchrow_array(); - $dbh->do("LOCK TABLES - groups WRITE, - profiles READ, - user_group_map WRITE"); + $dbh->bz_lock_tables('groups WRITE', 'profiles READ', + 'user_group_map WRITE'); $sth = $dbh->prepare("SELECT user_group_map.user_id, profiles.login_name FROM user_group_map, profiles WHERE user_group_map.user_id = profiles.userid @@ -516,7 +514,7 @@ if (($action eq 'remove_all_regexp') || ($action eq 'remove_all')) { SET last_changed = NOW() WHERE id = ?"); $sth->execute($gid); - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'users'} = \@users; $vars->{'name'} = $name; @@ -545,9 +543,9 @@ sub doGroupChanges { my $dbh = Bugzilla->dbh; my $sth; - $dbh->do("LOCK TABLES groups WRITE, group_group_map WRITE, - user_group_map WRITE, profiles READ, - namedqueries READ, whine_queries READ"); + $dbh->bz_lock_tables('groups WRITE', 'group_group_map WRITE', + 'user_group_map WRITE', 'profiles READ', + 'namedqueries READ', 'whine_queries READ'); # Check that the given group ID and regular expression are valid. # If tests are successful, trimmed values are returned by CheckGroup*. @@ -651,6 +649,6 @@ sub doGroupChanges { # mark the changes SendSQL("UPDATE groups SET last_changed = NOW() WHERE id = $gid"); } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); return $gid, $chgs, $name, $regexp; } diff --git a/editmilestones.cgi b/editmilestones.cgi index 4da121848..7364d4d06 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -360,11 +360,11 @@ if ($action eq 'delete') { # lock the tables before we start to change everything: - $dbh->do('LOCK TABLES attachments WRITE, - bugs WRITE, - bugs_activity WRITE, - milestones WRITE, - dependencies WRITE'); + $dbh->bz_lock_tables('attachments WRITE', + 'bugs WRITE', + 'bugs_activity WRITE', + 'milestones WRITE', + 'dependencies WRITE'); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries @@ -425,7 +425,7 @@ if ($action eq 'delete') { $product_id, $milestone); - $dbh->do('UNLOCK TABLES'); + $dbh->bz_unlock_tables(); unlink "$datadir/versioncache"; @@ -497,9 +497,9 @@ if ($action eq 'update') { my $dbh = Bugzilla->dbh; - $dbh->do("LOCK TABLES bugs WRITE, - milestones WRITE, - products WRITE"); + $dbh->bz_lock_tables('bugs WRITE', + 'milestones WRITE', + 'products WRITE'); # Need to store because detaint_natural() will delete this if # invalid @@ -507,7 +507,7 @@ if ($action eq 'update') { if ($sortkey != $sortkeyold) { if (!detaint_natural($sortkey)) { - $dbh->do('UNLOCK TABLES'); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('milestone_sortkey_invalid', {'name' => $milestone, 'sortkey' => $stored_sortkey}); @@ -532,12 +532,12 @@ if ($action eq 'update') { if ($milestone ne $milestoneold) { unless ($milestone) { - $dbh->do('UNLOCK TABLES'); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('milestone_blank_name'); exit; } if (TestMilestone($product, $milestone)) { - $dbh->do('UNLOCK TABLES'); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('milestone_already_exists', {'name' => $milestone, 'product' => $product}); @@ -579,7 +579,7 @@ if ($action eq 'update') { $vars->{'updated_name'} = 1; } - $dbh->do('UNLOCK TABLES'); + $dbh->bz_unlock_tables(); $vars->{'name'} = $milestone; $vars->{'product'} = $product; diff --git a/editproducts.cgi b/editproducts.cgi index 0fa2ddbae..f066f7029 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -231,7 +231,6 @@ sub EmitFormElements ($$$$$$$$$) sub PutTrailer (@) { my (@links) = ("Back to the query page", @_); - SendSQL("UNLOCK TABLES"); my $count = $#links; my $num = 0; @@ -281,6 +280,7 @@ my $headerdone = 0; my $localtrailer = "edit more products"; my $classhtmlvarstart = ""; my $classhtmlvar = ""; +my $dbh = Bugzilla->dbh; if (Param('useclassification') && (defined $classification)) { $classhtmlvar = "&classification=" . url_quote($classification); @@ -336,7 +336,6 @@ unless ($action) { CheckClassificationNew($classification); } - my $dbh = Bugzilla->dbh; my @execute_params = (); my @products = (); @@ -786,19 +785,19 @@ if ($action eq 'delete') { # lock the tables before we start to change everything: - SendSQL("LOCK TABLES attachments WRITE, - bugs WRITE, - bugs_activity WRITE, - components WRITE, - dependencies WRITE, - versions WRITE, - products WRITE, - groups WRITE, - group_control_map WRITE, - profiles WRITE, - milestones WRITE, - flaginclusions WRITE, - flagexclusions WRITE"); + $dbh->bz_lock_tables('attachments WRITE', + 'bugs WRITE', + 'bugs_activity WRITE', + 'components WRITE', + 'dependencies WRITE', + 'versions WRITE', + 'products WRITE', + 'groups WRITE', + 'group_control_map WRITE', + 'profiles WRITE', + 'milestones WRITE', + 'flaginclusions WRITE', + 'flagexclusions WRITE'); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries @@ -854,6 +853,8 @@ if ($action eq 'delete') { WHERE id=$product_id"); print "Product '$product' deleted.
\n"; + $dbh->bz_unlock_tables(); + unlink "$datadir/versioncache"; PutTrailer($localtrailer); exit; @@ -1107,12 +1108,12 @@ if ($action eq 'updategroupcontrols') { header_done => 1}); } } - SendSQL("LOCK TABLES groups READ, - group_control_map WRITE, - bugs WRITE, - bugs_activity WRITE, - bug_group_map WRITE, - fielddefs READ"); + $dbh->bz_lock_tables('groups READ', + 'group_control_map WRITE', + 'bugs WRITE', + 'bugs_activity WRITE', + 'bug_group_map WRITE', + 'fielddefs READ'); SendSQL("SELECT id, name, entry, membercontrol, othercontrol, canedit " . "FROM groups " . "LEFT JOIN group_control_map " . @@ -1234,6 +1235,8 @@ if ($action eq 'updategroupcontrols') { } print "added $count bugs

\n"; } + $dbh->bz_unlock_tables(); + print "Group control updates done

\n"; PutTrailer($localtrailer); @@ -1289,12 +1292,12 @@ if ($action eq 'update') { # Note that we got the $product_id using $productold above so it will # remain static even after we rename the product in the database. - SendSQL("LOCK TABLES products WRITE, - versions READ, - groups WRITE, - group_control_map WRITE, - profiles WRITE, - milestones READ"); + $dbh->bz_lock_tables('products WRITE', + 'versions READ', + 'groups WRITE', + 'group_control_map WRITE', + 'profiles WRITE', + 'milestones READ'); if ($disallownew ne $disallownewold) { $disallownew = $disallownew ? 1 : 0; @@ -1307,6 +1310,7 @@ if ($action eq 'update') { if ($description ne $descriptionold) { unless ($description) { print "Sorry, I can't delete the description."; + $dbh->bz_unlock_tables(UNLOCK_ABORT); PutTrailer($localtrailer); exit; } @@ -1357,6 +1361,7 @@ if ($action eq 'update') { " AND product_id = $product_id"); if (!FetchOneColumn()) { print "Sorry, the milestone $defaultmilestone must be defined first."; + $dbh->bz_unlock_tables(UNLOCK_ABORT); PutTrailer($localtrailer); exit; } @@ -1372,7 +1377,7 @@ if ($action eq 'update') { if ($product ne $productold) { unless ($product) { print "Sorry, I can't delete the product name."; - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); PutTrailer($localtrailer); exit; } @@ -1380,7 +1385,7 @@ if ($action eq 'update') { if (lc($product) ne lc($productold) && TestProduct($product)) { print "Sorry, product name '$product' is already in use."; - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); PutTrailer($localtrailer); exit; } @@ -1388,8 +1393,8 @@ if ($action eq 'update') { SendSQL("UPDATE products SET name=$qp WHERE id=$product_id"); print "Updated product name.
\n"; } + $dbh->bz_unlock_tables(); unlink "$datadir/versioncache"; - SendSQL("UNLOCK TABLES"); if ($checkvotes) { # 1. too many votes for a single user on a single bug. diff --git a/editversions.cgi b/editversions.cgi index 222e7dd8e..ee4a83d77 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -39,6 +39,7 @@ use Bugzilla::Config qw(:DEFAULT $datadir); use vars qw($template $vars); my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; # TestProduct: just returns if the specified product does exists # CheckProduct: same check, optionally emit an error text @@ -303,11 +304,11 @@ if ($action eq 'delete') { # lock the tables before we start to change everything: - SendSQL("LOCK TABLES attachments WRITE, - bugs WRITE, - bugs_activity WRITE, - versions WRITE, - dependencies WRITE"); + $dbh->bz_lock_tables('attachments WRITE', + 'bugs WRITE', + 'bugs_activity WRITE', + 'versions WRITE', + 'dependencies WRITE'); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries @@ -347,7 +348,7 @@ if ($action eq 'delete') { WHERE product_id = $product_id AND value = " . SqlQuote($version)); - SendSQL("UNLOCK TABLES;"); + $dbh->bz_unlock_tables(); unlink "$datadir/versioncache"; @@ -399,18 +400,18 @@ if ($action eq 'update') { # Note that the order of this tests is important. If you change # them, be sure to test for WHERE='$version' or WHERE='$versionold' - SendSQL("LOCK TABLES bugs WRITE, - versions WRITE, - products READ"); + $dbh->bz_lock_tables('bugs WRITE', + 'versions WRITE', + 'products READ'); if ($version ne $versionold) { unless ($version) { - SendSQL('UNLOCK TABLES'); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('version_blank_name'); exit; } if (TestVersion($product,$version)) { - SendSQL('UNLOCK TABLES'); + $dbh->bz_unlock_tables(UNLOCK_ABORT); ThrowUserError('version_already_exists', {'name' => $version, 'product' => $product}); @@ -430,7 +431,7 @@ if ($action eq 'update') { $vars->{'updated_name'} = 1; } - SendSQL('UNLOCK TABLES'); + $dbh->bz_unlock_tables(); $vars->{'name'} = $version; $vars->{'product'} = $product; diff --git a/process_bug.cgi b/process_bug.cgi index 6daa6d64e..abbad43f9 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -60,6 +60,7 @@ my $user = Bugzilla->login(LOGIN_REQUIRED); my $whoid = $user->id; my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; my $requiremilestone = 0; @@ -1134,22 +1135,22 @@ foreach my $id (@idlist) { $bug_changed = 0; 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, " . - "products READ, components READ, " . - "keywords $write, longdescs $write, fielddefs $write, " . - "bug_group_map $write, flags $write, duplicates $write," . + $dbh->bz_lock_tables("bugs $write", "bugs_activity $write", + "cc $write", "cc AS selectVisible_cc $write", + "profiles $write", "dependencies $write", "votes $write", + "products READ", "components READ", + "keywords $write", "longdescs $write", "fielddefs $write", + "bug_group_map $write", "flags $write", "duplicates $write", # user_group_map would be a READ lock except that Flag::process # may call Flag::notify, which creates a new user object, # which might call derive_groups, which wants a WRITE lock on that # table. group_group_map is in here at all because derive_groups # needs it. - "user_group_map $write, group_group_map READ, flagtypes READ, " . - "flaginclusions AS i READ, flagexclusions AS e READ, " . - "keyworddefs READ, groups READ, attachments READ, " . - "group_control_map AS oldcontrolmap READ, " . - "group_control_map AS newcontrolmap READ, " . + "user_group_map $write", "group_group_map READ", "flagtypes READ", + "flaginclusions AS i READ", "flagexclusions AS e READ", + "keyworddefs READ", "groups READ", "attachments READ", + "group_control_map AS oldcontrolmap READ", + "group_control_map AS newcontrolmap READ", "group_control_map READ"); # Fun hack. @::log_columns only contains the component_id, # not the name (since bug 43600 got fixed). So, we need to have @@ -1270,7 +1271,7 @@ foreach my $id (@idlist) { $vars->{'bug_id'} = $id; - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(UNLOCK_ABORT); # Warn the user about the mid-air collision and ask them what to do. $template->process("bug/process/midair.html.tmpl", $vars) @@ -1773,7 +1774,7 @@ foreach my $id (@idlist) { if ($bug_changed) { SendSQL("UPDATE bugs SET delta_ts = $sql_timestamp WHERE bug_id = $id"); } - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'mailrecipients'} = { 'cc' => \@ccRemoved, 'owner' => $origOwner, diff --git a/query.cgi b/query.cgi index 2715b5549..2806cdd24 100755 --- a/query.cgi +++ b/query.cgi @@ -88,7 +88,7 @@ if ($userid) { # If the query name contains invalid characters, don't import. $name =~ /[<>&]/ && next; trick_taint($name); - $dbh->do("LOCK TABLES namedqueries WRITE"); + $dbh->bz_lock_tables('namedqueries WRITE'); my $query = $dbh->selectrow_array( "SELECT query FROM namedqueries " . "WHERE userid = ? AND name = ?", @@ -98,7 +98,7 @@ if ($userid) { "(userid, name, query) VALUES " . "(?, ?, ?)", undef, ($userid, $name, $value)); } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } $cgi->send_cookie(-name => $cookiename, -expires => "Fri, 01-Jan-2038 00:00:00 GMT"); @@ -358,7 +358,7 @@ $vars->{'bug_severity'} = \@::legal_severity; # Boolean charts my @fields; push(@fields, { name => "noop", description => "---" }); -push(@fields, Bugzilla->dbh->bz_get_field_defs()); +push(@fields, $dbh->bz_get_field_defs()); $vars->{'fields'} = \@fields; # Creating new charts - if the cmd-add value is there, we define the field diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 7bd42d22d..81afe79c5 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -71,6 +71,7 @@ sub BugListLinks { Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; # Make sure the user is authorized to access sanitycheck.cgi. Access # is restricted to logged-in users who have "editbugs" privileges, @@ -95,7 +96,7 @@ PutHeader("Bugzilla Sanity Check"); if (defined $cgi->param('rebuildvotecache')) { Status("OK, now rebuilding vote cache."); - SendSQL("LOCK TABLES bugs WRITE, votes READ"); + $dbh->bz_lock_tables('bugs WRITE', 'votes READ'); SendSQL("UPDATE bugs SET votes = 0"); SendSQL("SELECT bug_id, SUM(vote_count) FROM votes GROUP BY bug_id"); my %votes; @@ -106,7 +107,7 @@ if (defined $cgi->param('rebuildvotecache')) { foreach my $id (keys %votes) { SendSQL("UPDATE bugs SET votes = $votes{$id} WHERE bug_id = $id"); } - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); Status("Vote cache has been rebuilt."); } @@ -148,7 +149,7 @@ if (defined $cgi->param('cleangroupsnow')) { Status("Cutoff is $cutoff"); SendSQL("SELECT COUNT(*) FROM user_group_map"); (my $before) = FetchSQLData(); - SendSQL("LOCK TABLES user_group_map WRITE, profiles WRITE"); + $dbh->bz_lock_tables('user_group_map WRITE', 'profiles WRITE'); SendSQL("SELECT userid FROM profiles " . "WHERE refreshed_when > 0 " . "AND refreshed_when < " . SqlQuote($cutoff) . @@ -162,7 +163,7 @@ if (defined $cgi->param('cleangroupsnow')) { SendSQL("UPDATE profiles SET refreshed_when = 0 WHERE userid = $id"); PopGlobalSQLState(); } - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); SendSQL("SELECT COUNT(*) FROM user_group_map"); (my $after) = FetchSQLData(); Status("Cleaned table for $count users " . @@ -537,7 +538,8 @@ Status("Checking cached keywords"); my %realk; if (defined $cgi->param('rebuildkeywordcache')) { - SendSQL("LOCK TABLES bugs write, keywords read, keyworddefs read"); + $dbh->bz_lock_tables('bugs write', 'keywords read', + 'keyworddefs read'); } SendSQL("SELECT keywords.bug_id, keyworddefs.name " . @@ -596,7 +598,7 @@ if (@badbugs) { } if (defined $cgi->param('rebuildkeywordcache')) { - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } ########################################################################### diff --git a/token.cgi b/token.cgi index bf810834f..d8c3fe288 100755 --- a/token.cgi +++ b/token.cgi @@ -192,6 +192,8 @@ sub cancelChangePassword { } sub changePassword { + my $dbh = Bugzilla->dbh; + # Quote the password and token for inclusion into SQL statements. my $cryptedpassword = bz_crypt($cgi->param('password')); my $quotedpassword = SqlQuote($cryptedpassword); @@ -202,12 +204,12 @@ sub changePassword { # Update the user's password in the profiles table and delete the token # from the tokens table. - SendSQL("LOCK TABLES profiles WRITE , tokens WRITE"); + $dbh->bz_lock_tables('profiles WRITE', 'tokens WRITE'); SendSQL("UPDATE profiles SET cryptpassword = $quotedpassword WHERE userid = $userid"); SendSQL("DELETE FROM tokens WHERE token = $::quotedtoken"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); Bugzilla->logout_user_by_id($userid); @@ -229,6 +231,7 @@ sub confirmChangeEmail { } sub changeEmail { + my $dbh = Bugzilla->dbh; # Get the user's ID from the tokens table. SendSQL("SELECT userid, eventdata FROM tokens @@ -251,14 +254,14 @@ sub changeEmail { # Update the user's login name in the profiles table and delete the token # from the tokens table. - SendSQL("LOCK TABLES profiles WRITE , tokens WRITE"); + $dbh->bz_lock_tables('profiles WRITE', 'tokens WRITE'); SendSQL("UPDATE profiles SET login_name = $quotednewemail WHERE userid = $userid"); SendSQL("DELETE FROM tokens WHERE token = $::quotedtoken"); SendSQL("DELETE FROM tokens WHERE userid = $userid AND tokentype = 'emailnew'"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # The email address has been changed, so we need to rederive the groups my $user = new Bugzilla::User($userid); @@ -276,6 +279,8 @@ sub changeEmail { } sub cancelChangeEmail { + my $dbh = Bugzilla->dbh; + # Get the user's ID from the tokens table. SendSQL("SELECT userid, tokentype, eventdata FROM tokens WHERE token = $::quotedtoken"); @@ -292,11 +297,11 @@ sub cancelChangeEmail { if($actualemail ne $old_email) { my $quotedoldemail = SqlQuote($old_email); - SendSQL("LOCK TABLES profiles WRITE"); + $dbh->bz_lock_tables('profiles WRITE'); SendSQL("UPDATE profiles SET login_name = $quotedoldemail WHERE userid = $userid"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # email has changed, so rederive groups # Note that this is done _after_ the tables are unlocked @@ -318,11 +323,11 @@ sub cancelChangeEmail { $vars->{'new_email'} = $new_email; Bugzilla::Token::Cancel($::token, $vars->{'message'}); - SendSQL("LOCK TABLES tokens WRITE"); + $dbh->bz_lock_tables('tokens WRITE'); SendSQL("DELETE FROM tokens WHERE userid = $userid AND tokentype = 'emailold' OR tokentype = 'emailnew'"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # Return HTTP response headers. print $cgi->header(); diff --git a/userprefs.cgi b/userprefs.cgi index f62f02500..193a4d73f 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -195,7 +195,8 @@ sub DoEmail { sub SaveEmail { my $updateString = ""; my $cgi = Bugzilla->cgi; - + my $dbh = Bugzilla->dbh; + if (defined $cgi->param('ExcludeSelf')) { $updateString .= 'ExcludeSelf~on'; } else { @@ -226,7 +227,7 @@ sub SaveEmail { # we don't really care if anyone reads the watch table. So # some small amount of contention could be gotten rid of by # using user-defined locks rather than table locking. - SendSQL("LOCK TABLES watch WRITE, profiles READ"); + $dbh->bz_lock_tables('watch WRITE', 'profiles READ'); # what the db looks like now my $origWatchedUsers = new Bugzilla::RelationSet; @@ -244,7 +245,7 @@ sub SaveEmail { ($CCDELTAS[0] eq "") || SendSQL($CCDELTAS[0]); ($CCDELTAS[1] eq "") || SendSQL($CCDELTAS[1]); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); } } diff --git a/votes.cgi b/votes.cgi index 7e2caf2a8..3a22c90b0 100755 --- a/votes.cgi +++ b/votes.cgi @@ -118,8 +118,9 @@ sub show_bug { # doing the viewing, give them the option to edit them too. sub show_user { GetVersionTable(); - + my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; # If a bug_id is given, and we're editing, we'll add it to the votes list. $bug_id ||= ""; @@ -129,11 +130,11 @@ sub show_user { my $userid = Bugzilla->user->id; my $canedit = (Param('usevotes') && $userid == $who) ? 1 : 0; - - SendSQL("LOCK TABLES bugs READ, products READ, votes WRITE, - cc READ, bug_group_map READ, user_group_map READ, - cc AS selectVisible_cc READ, groups READ"); - + + $dbh->bz_lock_tables('bugs READ', 'products READ', 'votes WRITE', + 'cc READ', 'bug_group_map READ', 'user_group_map READ', + 'cc AS selectVisible_cc READ', 'groups READ'); + if ($canedit && $bug_id) { # Make sure there is an entry for this bug # in the vote table, just so that things display right. @@ -212,7 +213,7 @@ sub show_user { } SendSQL("DELETE FROM votes WHERE vote_count <= 0"); - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'canedit'} = $canedit; $vars->{'voting_user'} = { "login" => $name }; @@ -231,6 +232,7 @@ sub record_votes { ############################################################################ my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; # Build a list of bug IDs for which votes have been submitted. Votes # are submitted in form fields in which the field names are the bug @@ -314,12 +316,13 @@ sub record_votes { # for products that only allow one vote per bug). In that case, we still # need to clear the user's votes from the database. my %affected; - SendSQL("LOCK TABLES bugs WRITE, bugs_activity WRITE, votes WRITE, - longdescs WRITE, profiles READ, products READ, components READ, - cc READ, dependencies READ, groups READ, fielddefs READ, - namedqueries READ, whine_queries READ, watch READ, - profiles AS watchers READ, profiles AS watched READ, - user_group_map READ, bug_group_map READ"); + $dbh->bz_lock_tables('bugs WRITE', 'bugs_activity WRITE', + 'votes WRITE', 'longdescs WRITE', 'profiles READ', + 'products READ', 'components READ', 'cc READ', + 'dependencies READ', 'groups READ', 'fielddefs READ', + 'namedqueries READ', 'whine_queries READ', 'watch READ', + 'profiles AS watchers READ', 'profiles AS watched READ', + 'user_group_map READ', 'bug_group_map READ'); # Take note of, and delete the user's old votes from the database. SendSQL("SELECT bug_id FROM votes WHERE who = $who"); @@ -349,7 +352,7 @@ sub record_votes { $vars->{'header_done'} = 1 if $confirmed; } - SendSQL("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); $vars->{'votes_recorded'} = 1; } diff --git a/whine.pl b/whine.pl index 920b27d05..0e469e8ad 100755 --- a/whine.pl +++ b/whine.pl @@ -220,12 +220,11 @@ sub get_next_event { # Loop until there's something to return until (scalar keys %{$event}) { - $dbh->do("LOCK TABLE " . - "whine_schedules WRITE, " . - "whine_events READ, " . - "profiles READ, " . - "groups READ, " . - "user_group_map READ"); + $dbh->bz_lock_tables('whine_schedules WRITE', + 'whine_events READ', + 'profiles READ', + 'groups READ', + 'user_group_map READ'); # Get the event ID for the first pending schedule $sth_next_scheduled_event->execute; @@ -262,7 +261,7 @@ sub get_next_event { reset_timer($sid); } - $dbh->do("UNLOCK TABLES"); + $dbh->bz_unlock_tables(); # Only set $event if the user is allowed to do whining if ($owner->in_group('bz_canusewhines')) { -- cgit v1.2.3-65-gdbad From 62eecf24480520e204ab0057f8f7845c13f37c13 Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Sat, 19 Feb 2005 00:01:47 +0000 Subject: Bug 280494: Replace "SELECT LAST_INSERT_ID()" with Bugzilla::DB function call Patch By Tomas Kopal r=mkanat, a=justdave --- Bugzilla/Auth/Login/WWW/CGI.pm | 2 +- Bugzilla/Auth/Login/WWW/Env.pm | 4 +--- attachment.cgi | 6 +++--- checksetup.pl | 17 +++++------------ contrib/bug_email.pl | 5 ++--- editgroups.cgi | 3 +-- editproducts.cgi | 6 ++---- editusers.cgi | 3 +-- importxml.pl | 4 ++-- post_bug.cgi | 5 +++-- 10 files changed, 21 insertions(+), 34 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Auth/Login/WWW/CGI.pm b/Bugzilla/Auth/Login/WWW/CGI.pm index 10eb85f60..98999a368 100644 --- a/Bugzilla/Auth/Login/WWW/CGI.pm +++ b/Bugzilla/Auth/Login/WWW/CGI.pm @@ -72,7 +72,7 @@ sub login { VALUES (?, ?, NOW())", undef, $userid, $ipaddr); - my $logincookie = $dbh->selectrow_array("SELECT LAST_INSERT_ID()"); + my $logincookie = $dbh->bz_last_key('logincookies', 'cookie'); # Remember cookie only if admin has told so # or admin didn't forbid it and user told to remember. diff --git a/Bugzilla/Auth/Login/WWW/Env.pm b/Bugzilla/Auth/Login/WWW/Env.pm index abd176315..54e202bbf 100644 --- a/Bugzilla/Auth/Login/WWW/Env.pm +++ b/Bugzilla/Auth/Login/WWW/Env.pm @@ -116,9 +116,7 @@ sub login { "realname, disabledtext " . ") VALUES ( ?, ?, ?, '' )"); $sth->execute($env_email, '*', $env_realname); - $sth = $dbh->prepare("SELECT last_insert_id()"); - $sth->execute(); - $matched_userid = $sth->fetch->[0]; + $matched_userid = $dbh->bz_last_key('profiles', 'userid'); } } } diff --git a/attachment.cgi b/attachment.cgi index 5e10d8fee..3522f9e26 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -869,7 +869,8 @@ sub insert my ($data) = @_; # Insert a new attachment into the database. - + my $dbh = Bugzilla->dbh; + # Escape characters in strings that will be used in SQL statements. $filename = SqlQuote($filename); my $description = SqlQuote($::FORM{'description'}); @@ -886,8 +887,7 @@ sub insert VALUES ($::FORM{'bugid'}, $sql_timestamp, $filename, $description, $contenttype, $::FORM{'ispatch'}, $isprivate, $::userid, $thedata)"); # Retrieve the ID of the newly created attachment record. - SendSQL("SELECT LAST_INSERT_ID()"); - my $attachid = FetchOneColumn(); + my $attachid = $dbh->bz_last_key('attachments', 'attach_id'); # Insert a comment about the new attachment into the database. my $comment = "Created an attachment (id=$attachid)\n$::FORM{'description'}\n"; diff --git a/checksetup.pl b/checksetup.pl index 79095ee3a..388703ab1 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -2232,9 +2232,7 @@ sub AddGroup { VALUES (?, ?, ?, ?)'); $sth->execute($name, $desc, $userregexp, 0); - $sth = $dbh->prepare("select last_insert_id()"); - $sth->execute(); - my ($last) = $sth->fetchrow_array(); + my $last = $dbh->bz_last_key('groups', 'id'); return $last; } @@ -2459,9 +2457,8 @@ unless ($sth->rows) { 'bugzilla.", "", 0, 0, 0)'); # We could probably just assume that this is "1", but better # safe than sorry... - $sth = $dbh->prepare("SELECT LAST_INSERT_ID()"); - $sth->execute; - my ($product_id) = $sth->fetchrow_array; + my $product_id = $dbh->bz_last_key('products', 'id'); + $dbh->do(qq{INSERT INTO versions (value, product_id) VALUES ("other", $product_id)}); # note: since admin user is not yet known, components gets a 0 for # initialowner and this is fixed during final checks. @@ -2801,9 +2798,7 @@ if (GetFieldDef('bugs', 'long_desc')) { $dbh->quote($name) . ", " . $dbh->quote(bz_crypt('okthen')) . ", " . "'Account created only to maintain database integrity')"); - $s2 = $dbh->prepare("SELECT LAST_INSERT_ID()"); - $s2->execute(); - ($who) = ($s2->fetchrow_array()); + $who = $dbh->bz_last_key('profiles', 'userid'); } next; } else { @@ -2850,9 +2845,7 @@ if (GetFieldDef('bugs_activity', 'field')) { if (!$id) { $dbh->do("INSERT INTO fielddefs (name, description) VALUES " . "($q, $q)"); - $s2 = $dbh->prepare("SELECT LAST_INSERT_ID()"); - $s2->execute(); - ($id) = ($s2->fetchrow_array()); + $id = $dbh->bz_last_key('fielddefs', 'fieldid'); } $dbh->do("UPDATE bugs_activity SET fieldid = $id WHERE field = $q"); } diff --git a/contrib/bug_email.pl b/contrib/bug_email.pl index 1f2f28421..7fb6b533c 100755 --- a/contrib/bug_email.pl +++ b/contrib/bug_email.pl @@ -38,7 +38,7 @@ # # You need to work with bug_email.pl the MIME::Parser installed. # -# $Id: bug_email.pl,v 1.23 2005/02/08 16:51:03 travis%sedsystems.ca Exp $ +# $Id: bug_email.pl,v 1.24 2005/02/18 16:01:48 mkanat%kerio.com Exp $ ############################################################### # 02/12/2000 (SML) @@ -1151,8 +1151,7 @@ END if( ! $test ) { SendSQL($query); - SendSQL("select LAST_INSERT_ID()"); - $id = FetchOneColumn(); + $id = Bugzilla->dbh->bz_last_key('bugs', 'bug_id'); my $long_desc_query = "INSERT INTO longdescs SET bug_id=$id, who=$userid, bug_when=\'$bug_when\', thetext=" . SqlQuote($comment); SendSQL($long_desc_query); diff --git a/editgroups.cgi b/editgroups.cgi index c3be719c7..02f24b1e4 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -274,8 +274,7 @@ if ($action eq 'new') { "1," . SqlQuote($regexp) . ", " . $isactive . ", NOW())" ); - SendSQL("SELECT last_insert_id()"); - my $gid = FetchOneColumn(); + my $gid = $dbh->bz_last_key('groups', 'id'); my $admin = GroupNameToId('admin'); # Since we created a new group, give the "admin" group all privileges # initially. diff --git a/editproducts.cgi b/editproducts.cgi index f066f7029..11658c29a 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -506,8 +506,7 @@ if ($action eq 'new') { SqlQuote($votestoconfirm) . "," . SqlQuote($defaultmilestone) . "," . SqlQuote($classification_id) . ")"); - SendSQL("SELECT LAST_INSERT_ID()"); - my $product_id = FetchOneColumn(); + my $product_id = $dbh->bz_last_key('products', 'id'); SendSQL("INSERT INTO versions ( " . "value, product_id" . @@ -531,8 +530,7 @@ if ($action eq 'new') { "VALUES (" . SqlQuote($productgroup) . ", " . SqlQuote("Access to bugs in the $product product") . ", 1, NOW())"); - SendSQL("SELECT last_insert_id()"); - my $gid = FetchOneColumn(); + my $gid = $dbh->bz_last_key('groups', 'id'); my $admin = GroupNameToId('admin'); # If we created a new group, give the "admin" group priviledges # initially. diff --git a/editusers.cgi b/editusers.cgi index 9c8de6164..9a6de0d17 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -461,8 +461,7 @@ if ($action eq 'new') { #+++ send e-mail away print "OK, done.
\n"; - SendSQL("SELECT last_insert_id()"); - my ($newuserid) = FetchSQLData(); + my $newuserid = $dbh->bz_last_key('profiles', 'userid'); my $changeduser = new Bugzilla::User($newuserid); $changeduser->derive_groups(); diff --git a/importxml.pl b/importxml.pl index 21d962d41..3b3b24a17 100755 --- a/importxml.pl +++ b/importxml.pl @@ -162,6 +162,7 @@ $xml =~ s/^.+(<\?xml version.+)$/$1/s; my $parser = new XML::Parser(Style => 'Tree'); my $tree = $parser->parse($xml); +my $dbh = Bugzilla->dbh; my $maintainer; if (defined $tree->[1][0]->{'maintainer'}) { @@ -609,8 +610,7 @@ for (my $k=1 ; $k <= $bugqty ; $k++) { . join (",\n", @values) . "\n)\n"; SendSQL($query); - SendSQL("select LAST_INSERT_ID()"); - my $id = FetchOneColumn(); + my $id = $dbh->bz_last_key('bugs', 'bug_id'); if (defined $bug_fields{'cc'}) { foreach my $person (split(/[ ,]/, $bug_fields{'cc'})) { diff --git a/post_bug.cgi b/post_bug.cgi index 0f5abddb2..3e5b289a8 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -56,6 +56,8 @@ my $user = Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; + # do a match on the fields if applicable &Bugzilla::User::match_field ({ @@ -420,8 +422,7 @@ while (MoreSQLData()) { SendSQL($sql); # Get the bug ID back. -SendSQL("select LAST_INSERT_ID()"); -my $id = FetchOneColumn(); +my $id = $dbh->bz_last_key('bugs', 'bug_id'); # Add the group restrictions foreach my $grouptoadd (@groupstoadd) { -- cgit v1.2.3-65-gdbad From 30a52319c024fefb2b4e4aa7bd974e4af7af30cd Mon Sep 17 00:00:00 2001 From: "bugreport%peshkin.net" <> Date: Mon, 21 Feb 2005 01:03:09 +0000 Subject: Bug 252272: Allow extremely large attachments to be stored locally r=wurblzap.a=justdave --- Bugzilla/Attachment.pm | 12 ++++ Bugzilla/Config.pm | 4 +- attachment.cgi | 91 +++++++++++++++++++++---- checksetup.pl | 20 ++++++ defparams.pl | 11 +++ template/en/default/attachment/create.html.tmpl | 12 ++++ template/en/default/global/user-error.html.tmpl | 9 +++ 7 files changed, 143 insertions(+), 16 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index e7b3ffe86..5f491f315 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -33,6 +33,7 @@ package Bugzilla::Attachment; # Use the Flag module to handle flags. use Bugzilla::Flag; +use Bugzilla::Config qw(:locations); ############################################################################ # Functions @@ -92,6 +93,17 @@ sub query # Retrieve a list of flags for this attachment. $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'}, 'is_active' => 1 }); + + # A zero size indicates that the attachment is stored locally. + if ($a{'datasize'} == 0) { + my $attachid = $a{'attachid'}; + my $hash = ($attachid % 100) + 100; + $hash =~ s/.*(\d\d)$/group.$1/; + if (open(AH, "$attachdir/$hash/attachment.$attachid")) { + $a{'datasize'} = (stat(AH))[7]; + close(AH); + } + } # We will display the edit link if the user can edit the attachment; # ie the are the submitter, or they have canedit. diff --git a/Bugzilla/Config.pm b/Bugzilla/Config.pm index 5c070e372..3849f146b 100644 --- a/Bugzilla/Config.pm +++ b/Bugzilla/Config.pm @@ -55,6 +55,7 @@ use Bugzilla::Util; our $libpath = '.'; our $localconfig = "$libpath/localconfig"; our $datadir = "$libpath/data"; +our $attachdir = "$datadir/attachments"; our $templatedir = "$libpath/template"; our $webdotdir = "$datadir/webdot"; @@ -72,7 +73,8 @@ our $webdotdir = "$datadir/webdot"; ( admin => [qw(GetParamList UpdateParams SetParam WriteParams)], db => [qw($db_driver $db_host $db_port $db_name $db_user $db_pass $db_sock)], - locations => [qw($libpath $localconfig $datadir $templatedir $webdotdir)], + locations => [qw($libpath $localconfig $attachdir + $datadir $templatedir $webdotdir)], ); Exporter::export_ok_tags('admin', 'db', 'locations'); diff --git a/attachment.cgi b/attachment.cgi index 3522f9e26..0a296609b 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -40,6 +40,7 @@ use vars qw( # Include the Bugzilla CGI and general utility library. require "CGI.pl"; +use Bugzilla::Config qw(:locations); # Use these modules to handle flags. use Bugzilla::Constants; @@ -360,12 +361,18 @@ sub validateData { my $maxsize = $::FORM{'ispatch'} ? Param('maxpatchsize') : Param('maxattachmentsize'); $maxsize *= 1024; # Convert from K - - my $fh = $cgi->upload('data'); + my $fh; + # Skip uploading into a local variable if the user wants to upload huge + # attachments into local files. + if (!$::FORM{'bigfile'}) + { + $fh = $cgi->upload('data'); + } my $data; # We could get away with reading only as much as required, except that then # we wouldn't have a size to print to the error handler below. + if (!$::FORM{'bigfile'}) { # enable 'slurp' mode local $/; @@ -373,10 +380,11 @@ sub validateData } $data + || ($::FORM{'bigfile'}) || ThrowUserError("zero_length_file"); # Make sure the attachment does not exceed the maximum permitted size - my $len = length($data); + my $len = $data ? length($data) : 0; if ($maxsize && $len > $maxsize) { my $vars = { filesize => sprintf("%.0f", $len/1024) }; if ( $::FORM{'ispatch'} ) { @@ -504,6 +512,23 @@ sub view # Return the appropriate HTTP response headers. $filename =~ s/^.*[\/\\]//; my $filesize = length($thedata); + # A zero length attachment in the database means the attachment is + # stored in a local file + if ($filesize == 0) + { + my $attachid = $::FORM{'id'}; + my $hash = ($attachid % 100) + 100; + $hash =~ s/.*(\d\d)$/group.$1/; + if (open(AH, "$attachdir/$hash/attachment.$attachid")) { + binmode AH; + $filesize = (stat(AH))[7]; + } + } + if ($filesize == 0) + { + ThrowUserError("attachment_removed"); + } + # escape quotes and backslashes in the filename, per RFCs 2045/822 $filename =~ s/\\/\\\\/g; # escape backslashes @@ -513,7 +538,15 @@ sub view -content_disposition=> "inline; filename=\"$filename\"", -content_length => $filesize); - print $thedata; + if ($thedata) { + print $thedata; + } else { + while () { + print $_; + } + close(AH); + } + } sub interdiff @@ -771,7 +804,7 @@ sub viewall $privacy = "AND isprivate < 1 "; } SendSQL("SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), - mimetype, description, ispatch, isobsolete, isprivate, + mimetype, description, ispatch, isobsolete, isprivate, LENGTH(thedata) FROM attachments WHERE bug_id = $::FORM{'bugid'} $privacy ORDER BY attach_id"); @@ -779,7 +812,7 @@ sub viewall while (MoreSQLData()) { my %a; # the attachment hash - ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $a{'datasize'}) = FetchSQLData(); $a{'isviewable'} = isViewable($a{'contenttype'}); @@ -889,11 +922,39 @@ sub insert # Retrieve the ID of the newly created attachment record. my $attachid = $dbh->bz_last_key('attachments', 'attach_id'); + # If the file is to be stored locally, stream the file from the webserver + # to the local file without reading it into a local variable. + if ($::FORM{'bigfile'}) + { + my $fh = $cgi->upload('data'); + my $hash = ($attachid % 100) + 100; + $hash =~ s/.*(\d\d)$/group.$1/; + mkdir "$attachdir/$hash", 0770; + chmod 0770, "$attachdir/$hash"; + open(AH, ">$attachdir/$hash/attachment.$attachid"); + binmode AH; + my $sizecount = 0; + my $limit = (Param("maxlocalattachment") * 1048576); + while (<$fh>) { + print AH $_; + $sizecount += length($_); + if ($sizecount > $limit) { + close AH; + close $fh; + unlink "$attachdir/$hash/attachment.$attachid"; + ThrowUserError("local_file_too_large"); + } + } + close AH; + close $fh; + } + + # Insert a comment about the new attachment into the database. my $comment = "Created an attachment (id=$attachid)\n$::FORM{'description'}\n"; $comment .= ("\n" . $::FORM{'comment'}) if $::FORM{'comment'}; - AppendComment($::FORM{'bugid'}, + AppendComment($::FORM{'bugid'}, Bugzilla->user->login, $comment, $isprivate, @@ -906,7 +967,7 @@ sub insert SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($::FORM{'bugid'}, $obsolete_id, $::userid, $sql_timestamp, $fieldid, '0', '1')"); # If the obsolete attachment has pending flags, migrate them to the new attachment. - if (Bugzilla::Flag::count({ 'attach_id' => $obsolete_id , + if (Bugzilla::Flag::count({ 'attach_id' => $obsolete_id , 'status' => 'pending', 'is_active' => 1 })) { Bugzilla::Flag::migrate($obsolete_id, $attachid, $timestamp); @@ -1009,11 +1070,11 @@ sub edit # Get a list of flag types that can be set for this attachment. SendSQL("SELECT product_id, component_id FROM bugs WHERE bug_id = $bugid"); my ($product_id, $component_id) = FetchSQLData(); - my $flag_types = Bugzilla::FlagType::match({ 'target_type' => 'attachment' , - 'product_id' => $product_id , + my $flag_types = Bugzilla::FlagType::match({ 'target_type' => 'attachment' , + 'product_id' => $product_id , 'component_id' => $component_id }); foreach my $flag_type (@$flag_types) { - $flag_type->{'flags'} = Bugzilla::Flag::match({ 'type_id' => $flag_type->{'id'}, + $flag_type->{'flags'} = Bugzilla::Flag::match({ 'type_id' => $flag_type->{'id'}, 'attach_id' => $::FORM{'id'}, 'is_active' => 1 }); } @@ -1087,10 +1148,10 @@ sub update # Update the attachment record in the database. # Sets the creation timestamp to itself to avoid it being updated automatically. SendSQL("UPDATE attachments - SET description = $quoteddescription , - mimetype = $quotedcontenttype , + SET description = $quoteddescription , + mimetype = $quotedcontenttype , filename = $quotedfilename , - ispatch = $::FORM{'ispatch'} , + ispatch = $::FORM{'ispatch'}, isobsolete = $::FORM{'isobsolete'} , isprivate = $::FORM{'isprivate'} WHERE attach_id = $::FORM{'id'} @@ -1143,7 +1204,7 @@ sub update # Unlock all database tables now that we are finished updating the database. $dbh->bz_unlock_tables(); - # If the user submitted a comment while editing the attachment, + # If the user submitted a comment while editing the attachment, # add the comment to the bug. if ( $::FORM{'comment'} ) { diff --git a/checksetup.pl b/checksetup.pl index 9c35b5a98..2aa2a6cc1 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -904,6 +904,14 @@ unless (-d $datadir && -e "$datadir/nomail") { open FILE, '>>', "$datadir/mail"; close FILE; } + + unless (-d $attachdir) { + print "Creating local attachments directory ...\n"; + # permissions for non-webservergroup are fixed later on + mkdir $attachdir, 0770; + } + + # 2000-12-14 New graphing system requires a directory to put the graphs in # This code copied from what happens for the data dir above. # If the graphs dir is not present, we assume that they have been using @@ -1088,6 +1096,17 @@ END } } + if (!-e "$attachdir/.htaccess") { + print "Creating $attachdir/.htaccess...\n"; + open HTACCESS, ">$attachdir/.htaccess"; + print HTACCESS <<'END'; +# nothing in this directory is retrievable unless overriden by an .htaccess +# in a subdirectory; +deny from all +END + close HTACCESS; + chmod $fileperm, "$attachdir/.htaccess"; + } if (!-e "Bugzilla/.htaccess") { print "Creating Bugzilla/.htaccess...\n"; open HTACCESS, '>', 'Bugzilla/.htaccess'; @@ -1428,6 +1447,7 @@ if ($^O !~ /MSWin32/i) { fixPerms("$datadir/duplicates", $<, $webservergid, 027, 1); fixPerms("$datadir/mining", $<, $webservergid, 027, 1); fixPerms("$datadir/template", $<, $webservergid, 007, 1); # webserver will write to these + fixPerms($attachdir, $<, $webservergid, 007, 1); # webserver will write to these fixPerms($webdotdir, $<, $webservergid, 007, 1); fixPerms("$webdotdir/.htaccess", $<, $webservergid, 027); fixPerms("$datadir/params", $<, $webservergid, 017); diff --git a/defparams.pl b/defparams.pl index 3f91aabe2..99b942ce6 100644 --- a/defparams.pl +++ b/defparams.pl @@ -1269,6 +1269,17 @@ Reason: %reason% checker => \&check_numeric }, + { + name => 'maxlocalattachment', + desc => 'The maximum size (in Megabytes) of attachments identified by ' . + 'the user as "Big Files" to be stored locally on the webserver. ' . + 'If set to zero, attachments will never be kept on the local ' . + 'filesystem.', + type => 't', + default => '0', + checker => \&check_numeric + }, + { name => 'chartgroup', desc => 'The name of the group of users who can use the "New Charts" ' . diff --git a/template/en/default/attachment/create.html.tmpl b/template/en/default/attachment/create.html.tmpl index 82ad73ce1..43af6e638 100644 --- a/template/en/default/attachment/create.html.tmpl +++ b/template/en/default/attachment/create.html.tmpl @@ -65,6 +65,18 @@ + [% IF Param("maxlocalattachment") %] + + BigFile: + + + + + + [% END %] diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 6a29f975d..ac2cba6d3 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -156,6 +156,10 @@ [% title = "Access Denied" %] You are not authorized to access this attachment. + [% ELSIF error == "attachment_removed" %] + [% title = "Attachment Removed" %] + The attachment you are attempting to access has been removed. + [% ELSIF error == "bug_access_denied" %] [% title = "Access Denied" %] You are not authorized to access [% terms.bug %] #[% bug_id FILTER html %]. @@ -604,6 +608,11 @@ [% title = "Invalid Keyword Name" %] You may not use commas or whitespace in a keyword name. + [% ELSIF error == "local_file_too_large" %] + [% title = "Local File Too Large" %] + Local file uploads must not exceed + [% Param('maxlocalattachment') %] MB in size. + [% ELSIF error == "login_needed_for_password_change" %] [% title = "Login Name Required" %] You must enter a login name when requesting to change your password. -- cgit v1.2.3-65-gdbad From 63dde60072374b2f7ef2f756d4ab9dff66669793 Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Fri, 25 Feb 2005 07:37:47 +0000 Subject: Bug 280500: Replace "DATE_FORMAT()" with Bugzilla::DB function call Patch By Tomas Kopal r=mkanat, a=myk --- Bugzilla/Attachment.pm | 8 +++++--- Bugzilla/Bug.pm | 19 ++++++++++--------- CGI.pl | 7 ++++--- attachment.cgi | 7 +++++-- buglist.cgi | 2 +- globals.pl | 4 +++- request.cgi | 3 ++- whine.pl | 8 ++++++-- 8 files changed, 36 insertions(+), 22 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 5f491f315..8be92dcf2 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -67,6 +67,8 @@ sub query # "attachments" variable. my ($bugid) = @_; + my $dbh = Bugzilla->dbh; + my $in_editbugs = &::UserInGroup("editbugs"); &::SendSQL("SELECT product_id FROM bugs @@ -76,9 +78,9 @@ sub query # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. - &::SendSQL(" - SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), - mimetype, description, ispatch, isobsolete, isprivate, + &::SendSQL("SELECT attach_id, " . + $dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . + ", mimetype, description, ispatch, isobsolete, isprivate, submitter_id, LENGTH(thedata) FROM attachments WHERE bug_id = $bugid ORDER BY attach_id "); diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 5e25820e9..2f1df58bd 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -144,18 +144,19 @@ sub initBug { 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'), + qa_contact, status_whiteboard, " . + $dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . ", delta_ts, COALESCE(SUM(votes.vote_count), 0), reporter_accessible, cclist_accessible, - estimated_time, remaining_time, DATE_FORMAT(deadline,'%Y-%m-%d') - from bugs left join votes using(bug_id), + estimated_time, remaining_time, " . + $dbh->sql_date_format('deadline', '%Y-%m-%d') . ", + FROM bugs LEFT JOIN votes using(bug_id), classifications, products, components WHERE bugs.bug_id = ? AND classifications.id = products.classification_id AND products.id = bugs.product_id AND components.id = bugs.component_id - group by bugs.bug_id"; + GROUP BY bugs.bug_id"; my $bug_sth = $dbh->prepare($query); $bug_sth->execute($bug_id); @@ -534,11 +535,11 @@ sub GetComments { my @comments; my $sth = $dbh->prepare( "SELECT profiles.realname AS name, profiles.login_name AS email, - date_format(longdescs.bug_when,'%Y.%m.%d %H:%i') AS time, - longdescs.thetext AS body, longdescs.work_time, + " . $dbh->sql_date_format('longdescs.bug_when', '%Y.%m.%d %H:%i') . " + AS time, longdescs.thetext AS body, longdescs.work_time, isprivate, already_wrapped, - date_format(longdescs.bug_when,'%Y%m%d%H%i%s') - FROM longdescs, profiles + " . $dbh->sql_date_format('longdescs.bug_when', '%Y%m%d%H%i%s') . " + FROM longdescs, profiles WHERE profiles.userid = longdescs.who AND longdescs.bug_id = ? ORDER BY longdescs.bug_when"); diff --git a/CGI.pl b/CGI.pl index 1a8f09c2f..1b556bc71 100644 --- a/CGI.pl +++ b/CGI.pl @@ -317,6 +317,7 @@ sub LogActivityEntry { sub GetBugActivity { my ($id, $starttime) = (@_); my $datepart = ""; + my $dbh = Bugzilla->dbh; die "Invalid id: $id" unless $id=~/^\s*\d+\s*$/; @@ -333,9 +334,9 @@ sub GetBugActivity { my $query = " SELECT COALESCE(fielddefs.description, bugs_activity.fieldid), fielddefs.name, - bugs_activity.attach_id, - DATE_FORMAT(bugs_activity.bug_when,'%Y.%m.%d %H:%i:%s'), - bugs_activity.removed, bugs_activity.added, + bugs_activity.attach_id, " . + $dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s') . + ", bugs_activity.removed, bugs_activity.added, profiles.login_name FROM bugs_activity $suppjoins LEFT JOIN fielddefs ON bugs_activity.fieldid = fielddefs.fieldid, diff --git a/attachment.cgi b/attachment.cgi index 0a296609b..92d127e1a 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -800,11 +800,14 @@ sub viewall # Retrieve the attachments from the database and write them into an array # of hashes where each hash represents one attachment. my $privacy = ""; + my $dbh = Bugzilla->dbh; + if (Param("insidergroup") && !(UserInGroup(Param("insidergroup")))) { $privacy = "AND isprivate < 1 "; } - SendSQL("SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'), - mimetype, description, ispatch, isobsolete, isprivate, + SendSQL("SELECT attach_id, " . + $dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . ", + mimetype, description, ispatch, isobsolete, isprivate, LENGTH(thedata) FROM attachments WHERE bug_id = $::FORM{'bugid'} $privacy ORDER BY attach_id"); diff --git a/buglist.cgi b/buglist.cgi index fd01f10d5..bd9aa8af7 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -505,7 +505,7 @@ DefineColumn("remaining_time" , "bugs.remaining_time" , "Remaining Hou DefineColumn("actual_time" , "(SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id)) AS actual_time", "Actual Hours"); DefineColumn("percentage_complete","(100*((SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id))/((SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id))+bugs.remaining_time))) AS percentage_complete", "% Complete"); DefineColumn("relevance" , "relevance" , "Relevance" ); -DefineColumn("deadline" , "DATE_FORMAT(bugs.deadline, '%Y-%m-%d')", "Deadline"); +DefineColumn("deadline" , $dbh->sql_date_format('bugs.deadline', '%Y-%m-%d'), "Deadline"); ################################################################################ # Display Column Determination diff --git a/globals.pl b/globals.pl index f6f15566d..100c8fab6 100644 --- a/globals.pl +++ b/globals.pl @@ -971,7 +971,9 @@ sub GetLongDescriptionAsText { my $result = ""; my $count = 0; my $anyprivate = 0; - my ($query) = ("SELECT profiles.login_name, DATE_FORMAT(longdescs.bug_when,'%Y.%m.%d %H:%i'), " . + my $dbh = Bugzilla->dbh; + my ($query) = ("SELECT profiles.login_name, " . + $dbh->sql_date_format('longdescs.bug_when', '%Y.%m.%d %H:%i') . ", " . " longdescs.thetext, longdescs.isprivate, " . " longdescs.already_wrapped " . "FROM longdescs, profiles " . diff --git a/request.cgi b/request.cgi index b4b7e6766..fa37344e2 100755 --- a/request.cgi +++ b/request.cgi @@ -56,6 +56,7 @@ exit; sub queue { my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; validateStatus($cgi->param('status')); validateGroup($cgi->param('group')); @@ -75,7 +76,7 @@ sub queue { flags.attach_id, attachments.description, requesters.realname, requesters.login_name, requestees.realname, requestees.login_name, - DATE_FORMAT(flags.creation_date,'%Y.%m.%d %H:%i'), + " . $dbh->sql_date_format('flags.creation_date', '%Y.%m.%d %H:%i') . ", " . # Select columns that help us weed out secure bugs to which the user # should not have access. diff --git a/whine.pl b/whine.pl index 52fbe4e82..97378a178 100755 --- a/whine.pl +++ b/whine.pl @@ -118,12 +118,16 @@ if (open(NOMAIL, '<', "$datadir/nomail")) { } # get the current date and time from the database -$sth = $dbh->prepare( 'SELECT DATE_FORMAT( NOW(), "%y,%m,%e,%w,%k,%i")'); +$sth = $dbh->prepare('SELECT ' . $dbh->sql_date_format('NOW()', '%y,%m,%d,%a,%H,%i')); $sth->execute; -my ($now_year, $now_month, $now_day, $now_weekday, $now_hour, $now_minute) = +my ($now_year, $now_month, $now_day, $now_weekdayname, $now_hour, $now_minute) = split(',', $sth->fetchrow_array); $sth->finish; +# As DBs have different days numbering, use day name and convert it +# to the range 0-6 +my $now_weekday = index("SunMonTueWedThuFriSat", $now_weekdayname) / 3; + my @daysinmonth = qw(0 31 28 31 30 31 30 31 31 30 31 30 31); # Alter February in case of a leap year. This simple way to do it only # applies if you won't be looking at February of next year, which whining -- cgit v1.2.3-65-gdbad From 5f042e841a435995e62f0cc8e8c4e9bd9f2c9c15 Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Mon, 7 Mar 2005 21:55:33 +0000 Subject: Bug 283924: Move ValidateComment out of CGI.pl Patch By Max Kanat-Alexander r=LpSolit, a=justdave --- Bugzilla/Bug.pm | 15 +++++++++++++++ CGI.pl | 10 ---------- attachment.cgi | 1 + 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index b2261e1ee..b9229d98e 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -44,6 +44,13 @@ use Bugzilla::User; use Bugzilla::Util; use Bugzilla::Error; +use base qw(Exporter); +@Bugzilla::Bug::EXPORT = qw( + ValidateComment +); + +use constant MAX_COMMENT_LENGTH => 65535; + sub fields { # Keep this ordering in sync with bugzilla.dtd my @fields = qw(bug_id alias creation_ts short_desc delta_ts @@ -585,6 +592,14 @@ sub CountOpenDependencies { return @dependencies; } +sub ValidateComment ($) { + my ($comment) = @_; + + if (defined($comment) && length($comment) > MAX_COMMENT_LENGTH) { + ThrowUserError("comment_too_long"); + } +} + sub AUTOLOAD { use vars qw($AUTOLOAD); my $attr = $AUTOLOAD; diff --git a/CGI.pl b/CGI.pl index d650ea08e..652d6c03b 100644 --- a/CGI.pl +++ b/CGI.pl @@ -188,16 +188,6 @@ sub ValidateBugID { } } -sub ValidateComment { - # Make sure a comment is not too large (greater than 64K). - - my ($comment) = @_; - - if (defined($comment) && length($comment) > 65535) { - ThrowUserError("comment_too_long"); - } -} - sub PasswordForLogin { my ($login) = (@_); SendSQL("select cryptpassword from profiles where login_name = " . diff --git a/attachment.cgi b/attachment.cgi index 92d127e1a..64e30f64a 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -48,6 +48,7 @@ use Bugzilla::Flag; use Bugzilla::FlagType; use Bugzilla::User; use Bugzilla::Util; +use Bugzilla::Bug; # Check whether or not the user is logged in and, if so, set the $::userid Bugzilla->login(); -- cgit v1.2.3-65-gdbad From 672d71471ee2ddae6005a9259789899c41264823 Mon Sep 17 00:00:00 2001 From: "travis%sedsystems.ca" <> Date: Thu, 10 Mar 2005 00:18:03 +0000 Subject: Bug 282574 : use the new "auth_failure" error message for all authentication failures Patch by Frederic Buclin r=travis, wurblzap a=myk --- attachment.cgi | 7 +++-- buglist.cgi | 5 +++- doeditparams.cgi | 10 +++---- quips.cgi | 7 +++-- sanitycheck.cgi | 4 ++- template/en/default/global/user-error.html.tmpl | 37 +++++++++++-------------- 6 files changed, 35 insertions(+), 35 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 64e30f64a..5e4c520f5 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -204,9 +204,10 @@ sub validateID # Make sure the user is authorized to access this attachment's bug. ($bugid, my $isprivate) = FetchSQLData(); ValidateBugID($bugid); - if (($isprivate > 0 ) && Param("insidergroup") && - !(UserInGroup(Param("insidergroup")))) { - ThrowUserError("attachment_access_denied"); + if ($isprivate && Param("insidergroup")) { + UserInGroup(Param("insidergroup")) + || ThrowUserError("auth_failure", {action => "access", + object => "attachment"}); } # XXX shim code, kill $::FORM diff --git a/buglist.cgi b/buglist.cgi index 5eadd906e..18ccde0d7 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -73,7 +73,10 @@ my $dotweak = $::FORM{'tweak'} ? 1 : 0; # Log the user in if ($dotweak) { Bugzilla->login(LOGIN_REQUIRED); - UserInGroup("editbugs") || ThrowUserError("insufficient_privs_for_multi"); + UserInGroup("editbugs") + || ThrowUserError("auth_failure", {group => "editbugs", + action => "modify", + object => "multiple_bugs"}); GetVersionTable(); } else { diff --git a/doeditparams.cgi b/doeditparams.cgi index 679bd74e3..099b98404 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -37,12 +37,10 @@ my $cgi = Bugzilla->cgi; print $cgi->header(); -if (!UserInGroup("tweakparams")) { - print "

Sorry, you aren't a member of the 'tweakparams' group.

\n"; - print "And so, you aren't allowed to edit the parameters.\n"; - PutFooter(); - exit; -} +UserInGroup("tweakparams") + || ThrowUserError("auth_failure", {group => "tweakparams", + action => "modify", + object => "parameters"}); PutHeader("Saving new parameters"); diff --git a/quips.cgi b/quips.cgi index 0e0c13d08..dc0106450 100755 --- a/quips.cgi +++ b/quips.cgi @@ -119,9 +119,10 @@ if ($action eq 'approve') { } if ($action eq "delete") { - if (!UserInGroup('admin')) { - ThrowUserError("quips_edit_denied"); - } + UserInGroup("admin") + || ThrowUserError("auth_failure", {group => "admin", + action => "delete", + object => "quips"}); my $quipid = $cgi->param("quipid"); ThrowCodeError("need_quipid") unless $quipid =~ /(\d+)/; $quipid = $1; diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 63ddf181f..30a07be5b 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -82,7 +82,9 @@ my $dbh = Bugzilla->dbh; # prevents users with a legitimate interest in Bugzilla integrity # from accessing the script). UserInGroup("editbugs") - || ThrowUserError("sanity_check_access_denied"); + || ThrowUserError("auth_failure", {group => "editbugs", + action => "run", + object => "sanity_check"}); print "Content-type: text/html\n"; print "\n"; diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index fffcdf4c4..2d992dfce 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -17,7 +17,7 @@ # Rights Reserved. # # Contributor(s): Gervase Markham - # Frdric Buclin + # Frdric Buclin #%] [%# INTERFACE: @@ -113,8 +113,10 @@ [% END %] [% END %] - and so you aren't allowed to - [% IF action == "add" %] + [% IF group || reason %] and so [% END %] you are not authorized to + [% IF action == "access" %] + access + [% ELSIF action == "add" %] add new [% ELSIF action == "modify" %] modify @@ -122,13 +124,17 @@ delete [% ELSIF action == "edit" %] add, modify or delete + [% ELSIF action == "run" %] + run [% ELSIF action == "schedule" %] schedule [% ELSIF action == "use" %] use [% END %] - [% IF object == "charts" %] + [% IF object == "attachment" %] + this attachment + [% ELSIF object == "charts" %] the "New Charts" feature [% ELSIF object == "classifications" %] classifications @@ -142,12 +148,18 @@ keywords [% ELSIF object == "milestones" %] milestones + [% ELSIF object == "multiple_bugs" %] + multiple [% terms.bugs %] at once [% ELSIF object == "parameters" %] parameters [% ELSIF object == "products" %] products + [% ELSIF object == "quips" %] + quips [% ELSIF object == "reports" %] whine reports + [% ELSIF object == "sanity_check" %] + a sanity check [% ELSIF object == "user" %] the user you specified [% ELSIF object == "users" %] @@ -156,10 +168,6 @@ versions [% END %]. - [% ELSIF error == "attachment_access_denied" %] - [% title = "Access Denied" %] - You are not authorized to access this attachment. - [% ELSIF error == "attachment_removed" %] [% title = "Attachment Removed" %] The attachment you are attempting to access has been removed. @@ -548,11 +556,6 @@ [% ELSIF error == "insufficient_data_points" %] We don't have enough data points to make a graph (yet). - [% ELSIF error == "insufficient_privs_for_multi" %] - [% title = "Insufficient Privileges" %] - Sorry, you do not have sufficient privileges to edit multiple - [% terms.bugs %]. - [% ELSIF error == "invalid_attach_id" %] [% title = "Invalid Attachment ID" %] The attachment id [% attach_id FILTER html %] is invalid. @@ -918,10 +921,6 @@ [% title = "Quips Disabled" %] Quips are disabled. - [% ELSIF error == "quips_edit_denied" %] - [% title = "Permission Denied" %] - You do not have permission to edit quips. - [% ELSIF error == "reassign_to_empty" %] [% title = "Illegal Reassignment" %] To reassign [% terms.abug %], you must provide an address for @@ -945,10 +944,6 @@ [% title = "Summary Needed" %] You must enter a summary for this [% terms.bug %]. - [% ELSIF error == "sanity_check_access_denied" %] - [% title = "Access Denied" %] - You do not have the permissions necessary to run a sanity check. - [% ELSIF error == "search_content_without_matches" %] [% title = "Illegal Search" %] The "content" field can only be used with "matches" search -- cgit v1.2.3-65-gdbad From 4d71801196cfa28e221410602ba76cede1c1acbf Mon Sep 17 00:00:00 2001 From: "travis%sedsystems.ca" <> Date: Tue, 15 Mar 2005 23:57:19 +0000 Subject: Bug 180652 : Marking an attachment as obsolete should cancel all unfulfilled requests Patch by Frederic Buclin r=myk a=myk --- Bugzilla/Flag.pm | 139 +++++++++++++++++++++++++++++++++++++------------------ attachment.cgi | 29 ++++++------ 2 files changed, 109 insertions(+), 59 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 4b5f76fb0..6d1abfdd9 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -19,6 +19,7 @@ # # Contributor(s): Myk Melez # Jouni Heikniemi +# Frdric Buclin ################################################################################ # Module Initialization @@ -236,33 +237,47 @@ sub validate { } } +sub snapshot { + my ($bug_id, $attach_id) = @_; + + my $flags = match({ 'bug_id' => $bug_id, + 'attach_id' => $attach_id, + 'is_active' => 1 }); + my @summaries; + foreach my $flag (@$flags) { + my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; + $summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'}; + push(@summaries, $summary); + } + return @summaries; +} + sub process { # Processes changes to flags. # The target is the bug or attachment this flag is about, the timestamp # is the date/time the bug was last touched (so that changes to the flag - # can be stamped with the same date/time), the data is the form data - # with flag fields that the user submitted, the old bug is the bug record - # before the user made changes to it, and the new bug is the bug record - # after the user made changes to it. + # can be stamped with the same date/time), and the data is the form data + # with flag fields that the user submitted. - my ($target, $timestamp, $data, $oldbug, $newbug) = @_; + my ($target, $timestamp, $data) = @_; + + my $dbh = Bugzilla->dbh; + my $bug_id = $target->{'bug'}->{'id'}; + my $attach_id = $target->{'attachment'}->{'id'}; # Use the date/time we were given if possible (allowing calling code # to synchronize the comment's timestamp with those of other records). $timestamp = ($timestamp ? &::SqlQuote($timestamp) : "NOW()"); # Take a snapshot of flags before any changes. - my $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} , - 'attach_id' => $target->{'attachment'}->{'id'} , - 'is_active' => 1 }); - my @old_summaries; - foreach my $flag (@$flags) { - my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; - $summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'}; - push(@old_summaries, $summary); - } + my @old_summaries = snapshot($bug_id, $attach_id); + # Cancel old request flags if we are obsoleting an attachment. + if ($attach_id && $data->{'isobsolete'}) { + CancelRequests($bug_id, $attach_id); + } + # Create new flags and update existing flags. my $new_flags = FormToNewFlags($target, $data); foreach my $flag (@$new_flags) { create($flag, $timestamp) } @@ -270,57 +285,62 @@ sub process { # In case the bug's product/component has changed, clear flags that are # no longer valid. - &::SendSQL(" - SELECT flags.id + my $flag_ids = $dbh->selectcol_arrayref( + "SELECT flags.id FROM (flags INNER JOIN bugs ON flags.bug_id = bugs.bug_id) LEFT OUTER JOIN flaginclusions i ON (flags.type_id = i.type_id AND (bugs.product_id = i.product_id OR i.product_id IS NULL) AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) - WHERE bugs.bug_id = $target->{'bug'}->{'id'} + WHERE bugs.bug_id = ? AND flags.is_active = 1 - AND i.type_id IS NULL - "); - clear(&::FetchOneColumn()) while &::MoreSQLData(); - &::SendSQL(" - SELECT flags.id + AND i.type_id IS NULL", + undef, $bug_id); + + foreach my $flag_id (@$flag_ids) { + clear($flag_id); + } + + $flag_ids = $dbh->selectcol_arrayref( + "SELECT flags.id FROM flags, bugs, flagexclusions e - WHERE bugs.bug_id = $target->{'bug'}->{'id'} + WHERE bugs.bug_id = ? AND flags.bug_id = bugs.bug_id AND flags.type_id = e.type_id AND flags.is_active = 1 AND (bugs.product_id = e.product_id OR e.product_id IS NULL) - AND (bugs.component_id = e.component_id OR e.component_id IS NULL) - "); - clear(&::FetchOneColumn()) while &::MoreSQLData(); - - # Take a snapshot of flags after changes. - $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} , - 'attach_id' => $target->{'attachment'}->{'id'} , - 'is_active' => 1 }); - my @new_summaries; - foreach my $flag (@$flags) { - my $summary = $flag->{'type'}->{'name'} . $flag->{'status'}; - $summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'}; - push(@new_summaries, $summary); + AND (bugs.component_id = e.component_id OR e.component_id IS NULL)", + undef, $bug_id); + + foreach my $flag_id (@$flag_ids) { + clear($flag_id); } - my $old_summaries = join(", ", @old_summaries); - my $new_summaries = join(", ", @new_summaries); + # Take a snapshot of flags after changes. + my @new_summaries = snapshot($bug_id, $attach_id); + + update_activity($bug_id, $attach_id, $timestamp, \@old_summaries, \@new_summaries); +} + +sub update_activity { + my ($bug_id, $attach_id, $timestamp, $old_summaries, $new_summaries) = @_; + my $dbh = Bugzilla->dbh; + + $attach_id ||= 'NULL'; + $old_summaries = join(", ", @$old_summaries); + $new_summaries = join(", ", @$new_summaries); my ($removed, $added) = diff_strings($old_summaries, $new_summaries); if ($removed ne $added) { my $sql_removed = &::SqlQuote($removed); my $sql_added = &::SqlQuote($added); my $field_id = &::GetFieldID('flagtypes.name'); - my $attach_id = $target->{'attachment'}->{'id'} || 'NULL'; - &::SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, " . - "bug_when, fieldid, removed, added) VALUES " . - "($target->{'bug'}->{'id'}, $attach_id, $::userid, " . - "$timestamp, $field_id, $sql_removed, $sql_added)"); + $dbh->do("INSERT INTO bugs_activity + (bug_id, attach_id, who, bug_when, fieldid, removed, added) + VALUES ($bug_id, $attach_id, $::userid, $timestamp, + $field_id, $sql_removed, $sql_added)"); } } - sub create { # Creates a flag record in the database. @@ -642,6 +662,37 @@ sub notify { Bugzilla::BugMail::MessageToMTA($message); } +# Cancel all request flags from the attachment being obsoleted. +sub CancelRequests { + my ($bug_id, $attach_id, $timestamp) = @_; + my $dbh = Bugzilla->dbh; + + my $request_ids = + $dbh->selectcol_arrayref("SELECT flags.id + FROM flags + LEFT JOIN attachments ON flags.attach_id = attachments.attach_id + WHERE flags.attach_id = ? + AND flags.status = '?' + AND flags.is_active = 1 + AND attachments.isobsolete = 0", + undef, $attach_id); + + return if (!scalar(@$request_ids)); + + # Take a snapshot of flags before any changes. + my @old_summaries = snapshot($bug_id, $attach_id) if ($timestamp); + foreach my $flag (@$request_ids) { + clear($flag); + } + + # If $timestamp is undefined, do not update the activity table + return unless ($timestamp); + + # Take a snapshot of flags after any changes. + my @new_summaries = snapshot($bug_id, $attach_id); + update_activity($bug_id, $attach_id, $timestamp, \@old_summaries, \@new_summaries); +} + ################################################################################ # Private Functions ################################################################################ diff --git a/attachment.cgi b/attachment.cgi index 5e4c520f5..5f50efb03 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -968,15 +968,13 @@ sub insert # Make existing attachments obsolete. my $fieldid = GetFieldID('attachments.isobsolete'); foreach my $obsolete_id (@{$::MFORM{'obsolete'}}) { + # If the obsolete attachment has request flags, cancel them. + # This call must be done before updating the 'attachments' table. + Bugzilla::Flag::CancelRequests($::FORM{'bugid'}, $obsolete_id, $sql_timestamp); + SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $obsolete_id"); SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($::FORM{'bugid'}, $obsolete_id, $::userid, $sql_timestamp, $fieldid, '0', '1')"); - # If the obsolete attachment has pending flags, migrate them to the new attachment. - if (Bugzilla::Flag::count({ 'attach_id' => $obsolete_id , - 'status' => 'pending', - 'is_active' => 1 })) { - Bugzilla::Flag::migrate($obsolete_id, $attachid, $timestamp); - } } # Assign the bug to the user, if they are allowed to take it @@ -1150,8 +1148,17 @@ sub update my $quotedcontenttype = SqlQuote($::FORM{'contenttype'}); my $quotedfilename = SqlQuote($::FORM{'filename'}); + # Figure out when the changes were made. + SendSQL("SELECT NOW()"); + my $timestamp = FetchOneColumn(); + + # Update flags. These calls must be done before updating the + # 'attachments' table due to the deletion of request flags + # on attachments being obsoleted. + my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); + Bugzilla::Flag::process($target, $timestamp, \%::FORM); + # Update the attachment record in the database. - # Sets the creation timestamp to itself to avoid it being updated automatically. SendSQL("UPDATE attachments SET description = $quoteddescription , mimetype = $quotedcontenttype , @@ -1162,10 +1169,6 @@ sub update WHERE attach_id = $::FORM{'id'} "); - # Figure out when the changes were made. - SendSQL("SELECT NOW()"); - my $timestamp = FetchOneColumn(); - # Record changes in the activity table. my $sql_timestamp = SqlQuote($timestamp); if ($olddescription ne $::FORM{'description'}) { @@ -1202,10 +1205,6 @@ sub update VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $oldisprivate, $::FORM{'isprivate'})"); } - # Update flags. - my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); - Bugzilla::Flag::process($target, $timestamp, \%::FORM); - # Unlock all database tables now that we are finished updating the database. $dbh->bz_unlock_tables(); -- cgit v1.2.3-65-gdbad From 235eaf8436a7cb2f693c3fb8bc5d042c652c1c5f Mon Sep 17 00:00:00 2001 From: "travis%sedsystems.ca" <> Date: Wed, 16 Mar 2005 06:08:12 +0000 Subject: Fix nits on bug 180652 - Frederic Buclin --- Bugzilla/Flag.pm | 14 ++++---------- attachment.cgi | 7 ++++--- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 6d1abfdd9..ea60eebe4 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -273,7 +273,7 @@ sub process { # Take a snapshot of flags before any changes. my @old_summaries = snapshot($bug_id, $attach_id); - # Cancel old request flags if we are obsoleting an attachment. + # Cancel pending requests if we are obsoleting an attachment. if ($attach_id && $data->{'isobsolete'}) { CancelRequests($bug_id, $attach_id); } @@ -297,9 +297,7 @@ sub process { AND i.type_id IS NULL", undef, $bug_id); - foreach my $flag_id (@$flag_ids) { - clear($flag_id); - } + foreach my $flag_id (@$flag_ids) { clear($flag_id) } $flag_ids = $dbh->selectcol_arrayref( "SELECT flags.id @@ -312,9 +310,7 @@ sub process { AND (bugs.component_id = e.component_id OR e.component_id IS NULL)", undef, $bug_id); - foreach my $flag_id (@$flag_ids) { - clear($flag_id); - } + foreach my $flag_id (@$flag_ids) { clear($flag_id) } # Take a snapshot of flags after changes. my @new_summaries = snapshot($bug_id, $attach_id); @@ -681,9 +677,7 @@ sub CancelRequests { # Take a snapshot of flags before any changes. my @old_summaries = snapshot($bug_id, $attach_id) if ($timestamp); - foreach my $flag (@$request_ids) { - clear($flag); - } + foreach my $flag (@$request_ids) { clear($flag) } # If $timestamp is undefined, do not update the activity table return unless ($timestamp); diff --git a/attachment.cgi b/attachment.cgi index 5f50efb03..bffba5bc4 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1152,9 +1152,10 @@ sub update SendSQL("SELECT NOW()"); my $timestamp = FetchOneColumn(); - # Update flags. These calls must be done before updating the - # 'attachments' table due to the deletion of request flags - # on attachments being obsoleted. + # Update flags. We have to do this before committing changes + # to attachments so that we can delete pending requests if the user + # is obsoleting this attachment without deleting any requests + # the user submits at the same time. my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); Bugzilla::Flag::process($target, $timestamp, \%::FORM); -- cgit v1.2.3-65-gdbad From 026808687250a3e1c2415c1967e1a48abeba217b Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Fri, 18 Mar 2005 11:23:54 +0000 Subject: Bug 285740: DBD::Pg must have the PG_BYTEA type specified for inserting BLOBs Patch By Max Kanat-Alexander r=Tomas.Kopal, a=justdave --- Bugzilla/DB.pm | 15 +++++++++++++++ Bugzilla/DB/Pg.pm | 2 ++ attachment.cgi | 14 +++++++++++--- 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 07e23f0e7..5256a5434 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -51,6 +51,16 @@ use Bugzilla::Error; use Bugzilla::DB::Schema; use Bugzilla::User; +##################################################################### +# Constants +##################################################################### + +use constant BLOB_TYPE => DBI::SQL_BLOB; + +##################################################################### +# Deprecated Functions +##################################################################### + # All this code is backwards compat fu. As such, its a bit ugly. Note the # circular dependencies on Bugzilla.pm # This is old cruft which will be removed, so theres not much use in @@ -787,6 +797,11 @@ constants are required to be subroutines or "use constant" variables. =over 4 +=item C + +The C<\%attr> argument that must be passed to bind_param in order to +correctly escape a C type. + =item C This is the minimum required version of the database server that the diff --git a/Bugzilla/DB/Pg.pm b/Bugzilla/DB/Pg.pm index be921f4d1..e635096f2 100644 --- a/Bugzilla/DB/Pg.pm +++ b/Bugzilla/DB/Pg.pm @@ -42,10 +42,12 @@ package Bugzilla::DB::Pg; use strict; use Bugzilla::Error; +use DBD::Pg; # This module extends the DB interface via inheritance use base qw(Bugzilla::DB); +use constant BLOB_TYPE => { pg_type => DBD::Pg::PG_BYTEA }; use constant REQUIRED_VERSION => '7.03.0000'; use constant PROGRAM_NAME => 'PostgreSQL'; use constant MODULE_NAME => 'Pg'; diff --git a/attachment.cgi b/attachment.cgi index bffba5bc4..054c8e62a 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -913,7 +913,6 @@ sub insert $filename = SqlQuote($filename); my $description = SqlQuote($::FORM{'description'}); my $contenttype = SqlQuote($::FORM{'contenttype'}); - my $thedata = SqlQuote($data); my $isprivate = $::FORM{'isprivate'} ? 1 : 0; # Figure out when the changes were made. @@ -921,8 +920,17 @@ sub insert my $sql_timestamp = SqlQuote($timestamp); # Insert the attachment into the database. - SendSQL("INSERT INTO attachments (bug_id, creation_ts, filename, description, mimetype, ispatch, isprivate, submitter_id, thedata) - VALUES ($::FORM{'bugid'}, $sql_timestamp, $filename, $description, $contenttype, $::FORM{'ispatch'}, $isprivate, $::userid, $thedata)"); + my $sth = $dbh->prepare("INSERT INTO attachments + (thedata, bug_id, creation_ts, filename, description, + mimetype, ispatch, isprivate, submitter_id) + VALUES (?, $::FORM{'bugid'}, $sql_timestamp, $filename, + $description, $contenttype, $::FORM{'ispatch'}, + $isprivate, $::userid)"); + # We only use $data here in this INSERT with a placeholder, + # so it's safe. + trick_taint($data); + $sth->bind_param(1, $data, $dbh->BLOB_TYPE); + $sth->execute(); # Retrieve the ID of the newly created attachment record. my $attachid = $dbh->bz_last_key('attachments', 'attach_id'); -- cgit v1.2.3-65-gdbad From 6f0e3792941892e2987294fb0866e69630675002 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Tue, 5 Apr 2005 01:40:59 +0000 Subject: Bug 288883: SQL crash when granting/denying a request - Patch by Frederic Buclin r=joel a=justdave --- attachment.cgi | 2 +- process_bug.cgi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 054c8e62a..65157d740 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1140,7 +1140,7 @@ sub update # Bugzilla::User needs to rederive groups. profiles and # user_group_map would be READ locks instead of WRITE locks if it # weren't for derive_groups, which needs to write to those tables. - 'bugs READ', 'profiles WRITE', + 'bugs READ', 'profiles WRITE', 'email_setting READ', 'cc READ', 'bug_group_map READ', 'user_group_map WRITE', 'group_group_map READ', 'groups READ'); diff --git a/process_bug.cgi b/process_bug.cgi index 55835ca3d..55f40bda3 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -1182,7 +1182,7 @@ foreach my $id (@idlist) { "keyworddefs READ", "groups READ", "attachments READ", "group_control_map AS oldcontrolmap READ", "group_control_map AS newcontrolmap READ", - "group_control_map READ"); + "group_control_map READ", "email_setting READ"); # Fun hack. @::log_columns only contains the component_id, # not the name (since bug 43600 got fixed). So, we need to have # this id ready for the loop below, otherwise anybody can -- cgit v1.2.3-65-gdbad From f5f31fc070588c2075dd13a0fbabe8117e3aad76 Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Tue, 5 Apr 2005 04:52:03 +0000 Subject: Bug 286235: Implicit joins should be replaced by explicit joins - installment A Patch By Tomas Kopal r=joel, a=myk --- CGI.pl | 32 +++++++++++++++++--------------- attachment.cgi | 15 +++++++++------ buglist.cgi | 25 ++++++++++++++----------- checksetup.pl | 12 ++++++------ collectstats.pl | 36 +++++++++++++++++++----------------- editclassifications.cgi | 8 +++++--- editcomponents.cgi | 7 ++++--- editflagtypes.cgi | 16 ++++++++++------ editgroups.cgi | 9 +++++---- editmilestones.cgi | 7 ++++--- process_bug.cgi | 28 ++++++++++++++-------------- request.cgi | 47 ++++++++++++++++++++++++----------------------- sanitycheck.cgi | 41 +++++++++++++++++++++++------------------ showdependencytree.cgi | 21 +++++++++++---------- summarize_time.cgi | 39 ++++++++++++++++++++++----------------- userprefs.cgi | 12 +++++++----- votes.cgi | 19 ++++++++++--------- whineatnews.pl | 12 +++++++----- 18 files changed, 211 insertions(+), 175 deletions(-) (limited to 'attachment.cgi') diff --git a/CGI.pl b/CGI.pl index d1c738ff9..f700d3702 100644 --- a/CGI.pl +++ b/CGI.pl @@ -176,7 +176,7 @@ sub ValidateBugID { sub PasswordForLogin { my ($login) = (@_); - SendSQL("select cryptpassword from profiles where login_name = " . + SendSQL("SELECT cryptpassword FROM profiles WHERE login_name = " . SqlQuote($login)); my $result = FetchOneColumn(); if (!defined $result) { @@ -223,8 +223,8 @@ sub CheckIfVotedConfirmed { PushGlobalSQLState(); SendSQL("SELECT bugs.votes, bugs.bug_status, products.votestoconfirm, " . " bugs.everconfirmed, NOW() " . - "FROM bugs, products " . - "WHERE bugs.bug_id = $id AND products.id = bugs.product_id"); + "FROM bugs INNER JOIN products ON products.id = bugs.product_id " . + "WHERE bugs.bug_id = $id"); my ($votes, $status, $votestoconfirm, $everconfirmed, $timestamp) = (FetchSQLData()); my $sql_timestamp = SqlQuote($timestamp); my $ret = 0; @@ -298,7 +298,7 @@ sub GetBugActivity { die "Invalid id: $id" unless $id=~/^\s*\d+\s*$/; if (defined $starttime) { - $datepart = "and bugs_activity.bug_when > " . SqlQuote($starttime); + $datepart = "AND bugs_activity.bug_when > " . SqlQuote($starttime); } my $suppjoins = ""; my $suppwhere = ""; @@ -309,17 +309,19 @@ sub GetBugActivity { } my $query = " SELECT COALESCE(fielddefs.description, bugs_activity.fieldid), - fielddefs.name, - bugs_activity.attach_id, " . - $dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s') . - ", bugs_activity.removed, bugs_activity.added, - profiles.login_name - FROM bugs_activity $suppjoins LEFT JOIN fielddefs ON - bugs_activity.fieldid = fielddefs.fieldid, - profiles - WHERE bugs_activity.bug_id = $id $datepart - AND profiles.userid = bugs_activity.who $suppwhere - ORDER BY bugs_activity.bug_when"; + fielddefs.name, bugs_activity.attach_id, " . + $dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s') . + ", bugs_activity.removed, bugs_activity.added, profiles.login_name + FROM bugs_activity + $suppjoins + LEFT JOIN fielddefs + ON bugs_activity.fieldid = fielddefs.fieldid + INNER JOIN profiles + ON profiles.userid = bugs_activity.who + WHERE bugs_activity.bug_id = $id + $datepart + $suppwhere + ORDER BY bugs_activity.bug_when"; SendSQL($query); diff --git a/attachment.cgi b/attachment.cgi index 65157d740..fadbe8b97 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -265,9 +265,10 @@ sub validateCanChangeAttachment { my ($attachid) = @_; SendSQL("SELECT product_id - FROM attachments, bugs - WHERE attach_id = $attachid - AND bugs.bug_id = attachments.bug_id"); + FROM attachments + INNER JOIN bugs + ON bugs.bug_id = attachments.bug_id + WHERE attach_id = $attachid"); my $productid = FetchOneColumn(); CanEditProductId($productid) || ThrowUserError("illegal_attachment_edit", @@ -993,9 +994,11 @@ sub insert my @fields = ("assigned_to", "bug_status", "resolution", "login_name"); # Get the old values, for the bugs_activity table - SendSQL("SELECT " . join(", ", @fields) . " FROM bugs, profiles " . - "WHERE bugs.bug_id = $::FORM{'bugid'} " . - "AND profiles.userid = bugs.assigned_to"); + SendSQL("SELECT " . join(", ", @fields) . " " . + "FROM bugs " . + "INNER JOIN profiles " . + "ON profiles.userid = bugs.assigned_to " . + "WHERE bugs.bug_id = $::FORM{'bugid'}"); my @oldvalues = FetchSQLData(); my @newvalues = ($::userid, "ASSIGNED", "", DBID_to_name($::userid)); diff --git a/buglist.cgi b/buglist.cgi index d6567852b..cfcefee90 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -307,9 +307,11 @@ sub GetGroupsByUserId { # the columns for that row. my $groups = $dbh->selectall_arrayref( "SELECT DISTINCT groups.id, name, description, isactive - FROM groups, user_group_map - WHERE user_id = ? AND isbless = 0 - AND user_group_map.group_id = groups.id + FROM groups + INNER JOIN user_group_map + ON user_group_map.group_id = groups.id + WHERE user_id = ? + AND isbless = 0 AND isbuggroup = 1 ORDER BY description " , {Slice => {}}, ($userid)); @@ -873,14 +875,15 @@ while (my @row = $buglist_sth->fetchrow_array()) { # or because of human choice my %min_membercontrol; if (@bugidlist) { - my $sth = $dbh->prepare("SELECT DISTINCT bugs.bug_id, " . - "MIN(group_control_map.membercontrol) " . - "FROM bugs, bug_group_map " . - "LEFT JOIN group_control_map " . - "ON group_control_map.product_id=bugs.product_id " . - "AND group_control_map.group_id=bug_group_map.group_id " . - "WHERE bugs.bug_id = bug_group_map.bug_id " . - "AND bugs.bug_id IN (" . join(',',@bugidlist) . ") " . + my $sth = $dbh->prepare( + "SELECT DISTINCT bugs.bug_id, MIN(group_control_map.membercontrol) " . + "FROM bugs " . + "INNER JOIN bug_group_map " . + "ON bugs.bug_id = bug_group_map.bug_id " . + "LEFT JOIN group_control_map " . + "ON group_control_map.product_id = bugs.product_id " . + "AND group_control_map.group_id = bug_group_map.group_id " . + "WHERE bugs.bug_id IN (" . join(',',@bugidlist) . ") " . $dbh->sql_group_by('bugs.bug_id')); $sth->execute(); while (my ($bug_id, $min_membercontrol) = $sth->fetchrow_array()) { diff --git a/checksetup.pl b/checksetup.pl index b4f9fb930..c46bcfec9 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -4090,7 +4090,7 @@ if (@admins) { my @groups = (); -$sth = $dbh->prepare("select id from groups"); +$sth = $dbh->prepare("SELECT id FROM groups"); $sth->execute(); while ( my @row = $sth->fetchrow_array() ) { push (@groups, $row[0]); @@ -4099,8 +4099,8 @@ while ( my @row = $sth->fetchrow_array() ) { # Prompt the user for the email address and name of an administrator. Create # that login, if it doesn't exist already, and make it a member of all groups. -$sth = $dbh->prepare("SELECT user_id FROM groups, user_group_map " . - "WHERE name = 'admin' AND id = group_id"); +$sth = $dbh->prepare("SELECT user_id FROM groups INNER JOIN user_group_map " . + "ON id = group_id WHERE name = 'admin'"); $sth->execute; # when we have no admin users, prompt for admin email address and password ... if ($sth->rows == 0) { @@ -4285,9 +4285,9 @@ if ($sth->rows == 0) { # Final checks... $sth = $dbh->prepare("SELECT user_id " . - "FROM groups, user_group_map " . - "WHERE groups.name = 'admin' " . - "AND groups.id = user_group_map.group_id"); + "FROM groups INNER JOIN user_group_map " . + "ON groups.id = user_group_map.group_id " . + "WHERE groups.name = 'admin'"); $sth->execute; my ($adminuid) = $sth->fetchrow_array; if (!$adminuid) { die "No administrator!" } # should never get here diff --git a/collectstats.pl b/collectstats.pl index ae44b0b75..eca072e61 100755 --- a/collectstats.pl +++ b/collectstats.pl @@ -275,9 +275,9 @@ sub regenerate_stats { my $from_product = ""; if ($product ne '-All-') { - $and_product = "AND bugs.product_id = products.id " . - "AND products.name = " . SqlQuote($product) . " "; - $from_product = ", products"; + $and_product = " AND products.name = " . SqlQuote($product); + $from_product = "INNER JOIN products " . + "ON bugs.product_id = products.id"; } # Determine the start date from the date the first bug in the @@ -287,9 +287,9 @@ sub regenerate_stats { $dbh->sql_to_days('current_date') . " AS end, " . $dbh->sql_to_days("'1970-01-01'") . " FROM bugs $from_product WHERE " . - $dbh->sql_to_days('creation_ts') . " != 'NULL' " . + $dbh->sql_to_days('creation_ts') . " != 'NULL'" . $and_product . - "ORDER BY start " . $dbh->sql_limit(1)); + " ORDER BY start " . $dbh->sql_limit(1)); my ($start, $end, $base) = FetchSQLData(); if (!defined $start) { @@ -350,12 +350,13 @@ FIN for my $bug (@bugs) { # First, get information on various bug states. SendSQL("SELECT bugs_activity.removed " . - "FROM bugs_activity,fielddefs " . - "WHERE bugs_activity.fieldid = fielddefs.fieldid " . - "AND fielddefs.name = 'bug_status' " . - "AND bugs_activity.bug_id = $bug " . - "AND bugs_activity.bug_when >= from_days($day) " . - "ORDER BY bugs_activity.bug_when " . + " FROM bugs_activity " . + "INNER JOIN fielddefs " . + " ON bugs_activity.fieldid = fielddefs.fieldid " . + " WHERE fielddefs.name = 'bug_status' " . + " AND bugs_activity.bug_id = $bug " . + " AND bugs_activity.bug_when >= from_days($day) " . + "ORDER BY bugs_activity.bug_when " . $dbh->sql_limit(1)); my $status; @@ -372,12 +373,13 @@ FIN # Next, get information on various bug resolutions. SendSQL("SELECT bugs_activity.removed " . - "FROM bugs_activity,fielddefs " . - "WHERE bugs_activity.fieldid = fielddefs.fieldid " . - "AND fielddefs.name = 'resolution' " . - "AND bugs_activity.bug_id = $bug " . - "AND bugs_activity.bug_when >= from_days($day) " . - "ORDER BY bugs_activity.bug_when " . + " FROM bugs_activity " . + "INNER JOIN fielddefs " . + " ON bugs_activity.fieldid = fielddefs.fieldid " . + " WHERE fielddefs.name = 'resolution' " . + " AND bugs_activity.bug_id = $bug " . + " AND bugs_activity.bug_when >= from_days($day) " . + "ORDER BY bugs_activity.bug_when " . $dbh->sql_limit(1)); if (@row = FetchSQLData()) { diff --git a/editclassifications.cgi b/editclassifications.cgi index 9dad9ae37..5f31c50a8 100755 --- a/editclassifications.cgi +++ b/editclassifications.cgi @@ -374,9 +374,11 @@ if ($action eq 'reclassify') { products.name, classifications.name, classifications.id > 1 as unknown - FROM products,classifications - WHERE classifications.id=products.classification_id - ORDER BY unknown, products.name, classifications.name"); + FROM products + INNER JOIN classifications + ON classifications.id = products.classification_id + ORDER BY unknown, products.name, + classifications.name"); $sth->execute(); while ( my ($clid, $name, $clname) = $sth->fetchrow_array() ) { if ($clid == $classification_id) { diff --git a/editcomponents.cgi b/editcomponents.cgi index a86329d9d..9dd290db3 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -81,9 +81,10 @@ sub TestComponent ($$) # does the product/component combination exist? SendSQL("SELECT components.name - FROM components, products - WHERE products.id = components.product_id - AND products.name = " . SqlQuote($prod) . " + FROM components + INNER JOIN products + ON products.id = components.product_id + WHERE products.name = " . SqlQuote($prod) . " AND components.name = " . SqlQuote($comp)); return FetchOneColumn(); } diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 7936823dc..8ae3a7408 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -329,12 +329,14 @@ sub update { # the list of inclusions or that have been added to the list of exclusions. SendSQL(" SELECT flags.id - FROM flags, bugs LEFT OUTER JOIN flaginclusions AS i - ON (flags.type_id = i.type_id + FROM flags + INNER JOIN bugs + ON flags.bug_id = bugs.bug_id + LEFT OUTER JOIN flaginclusions AS i + ON (flags.type_id = i.type_id AND (bugs.product_id = i.product_id OR i.product_id IS NULL) AND (bugs.component_id = i.component_id OR i.component_id IS NULL)) WHERE flags.type_id = $::FORM{'id'} - AND flags.bug_id = bugs.bug_id AND flags.is_active = 1 AND i.type_id IS NULL "); @@ -342,10 +344,12 @@ sub update { SendSQL(" SELECT flags.id - FROM flags, bugs, flagexclusions AS e + FROM flags + INNER JOIN bugs + ON flags.bug_id = bugs.bug_id + INNER JOIN flagexclusions AS e + ON flags.type_id = e.type_id WHERE flags.type_id = $::FORM{'id'} - AND flags.bug_id = bugs.bug_id - AND flags.type_id = e.type_id AND flags.is_active = 1 AND (bugs.product_id = e.product_id OR e.product_id IS NULL) AND (bugs.component_id = e.component_id OR e.component_id IS NULL) diff --git a/editgroups.cgi b/editgroups.cgi index 88099b543..87419a9cc 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -68,7 +68,7 @@ sub RederiveRegexp ($$) AND grant_type = ? and isbless = 0"); $sth->execute(); while (my ($uid, $login) = $sth->fetchrow_array()) { - my $present = $dbh->selectrow_array($sthqry, undef, + my $present = $dbh->selectrow_array($sthqry, undef, $uid, $gid, GRANT_REGEXP); if (($regexp =~ /\S+/) && ($login =~ m/$regexp/i)) { @@ -494,9 +494,10 @@ if (($action eq 'remove_all_regexp') || ($action eq 'remove_all')) { $dbh->bz_lock_tables('groups WRITE', 'profiles READ', 'user_group_map WRITE'); $sth = $dbh->prepare("SELECT user_group_map.user_id, profiles.login_name - FROM user_group_map, profiles - WHERE user_group_map.user_id = profiles.userid - AND user_group_map.group_id = ? + FROM user_group_map + INNER JOIN profiles + ON user_group_map.user_id = profiles.userid + WHERE user_group_map.group_id = ? AND grant_type = ? AND isbless = 0"); $sth->execute($gid, GRANT_DIRECT); diff --git a/editmilestones.cgi b/editmilestones.cgi index 0edbd1897..796643e9e 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -79,9 +79,10 @@ sub TestMilestone ($$) # does the product exist? my $sth = $dbh->prepare_cached(" SELECT products.name, value - FROM milestones, products - WHERE milestones.product_id = products.id - AND products.name = ? + FROM milestones + INNER JOIN products + ON milestones.product_id = products.id + WHERE products.name = ? AND value = ?"); trick_taint($product); diff --git a/process_bug.cgi b/process_bug.cgi index 55f40bda3..94d86c936 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -227,8 +227,8 @@ sub CheckonComment( $ ) { # and make the user verify the version, component, target milestone, # and bug groups if so. if ( $::FORM{'id'} ) { - SendSQL("SELECT name FROM products, bugs " . - "WHERE products.id = bugs.product_id AND bug_id = $::FORM{'id'}"); + SendSQL("SELECT name FROM products INNER JOIN bugs " . + "ON products.id = bugs.product_id WHERE bug_id = $::FORM{'id'}"); $::oldproduct = FetchSQLData(); } if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct) @@ -445,7 +445,7 @@ sub CheckCanChangeField { # At this point, the user is either the reporter or an # unprivileged user. We first check for fields the reporter - # is not allowed to change. + # is not allowed to change. # The reporter may not: # - reassign bugs, unless the bugs are assigned to him; @@ -673,10 +673,10 @@ sub ChangeResolution { my @groupAdd = (); my @groupDel = (); -SendSQL("SELECT groups.id, isactive FROM groups, user_group_map WHERE " . - "groups.id = user_group_map.group_id AND " . - "user_group_map.user_id = $whoid AND " . - "isbless = 0 AND isbuggroup = 1"); +SendSQL("SELECT groups.id, isactive FROM groups INNER JOIN user_group_map " . + "ON groups.id = user_group_map.group_id " . + "WHERE user_group_map.user_id = $whoid " . + "AND isbless = 0 AND isbuggroup = 1"); while (my ($b, $isactive) = FetchSQLData()) { # The multiple change page may not show all groups a bug is in # (eg product groups when listing more than one product) @@ -1099,8 +1099,8 @@ my $delta_ts; sub SnapShotBug { my ($id) = (@_); - SendSQL("select delta_ts, " . join(',', @::log_columns) . - " from bugs where bug_id = $id"); + SendSQL("SELECT delta_ts, " . join(',', @::log_columns) . + " FROM bugs WHERE bug_id = $id"); my @row = FetchSQLData(); $delta_ts = shift @row; @@ -1110,7 +1110,7 @@ sub SnapShotBug { sub SnapShotDeps { my ($i, $target, $me) = (@_); - SendSQL("select $target from dependencies where $me = $i order by $target"); + SendSQL("SELECT $target FROM dependencies WHERE $me = $i ORDER BY $target"); my @list; while (MoreSQLData()) { push(@list, FetchOneColumn()); @@ -1344,7 +1344,7 @@ foreach my $id (@idlist) { my @stack = @{$deps{$target}}; while (@stack) { my $i = shift @stack; - SendSQL("select $target from dependencies where $me = " . + SendSQL("SELECT $target FROM dependencies WHERE $me = " . SqlQuote($i)); while (MoreSQLData()) { my $t = FetchOneColumn(); @@ -1390,7 +1390,7 @@ foreach my $id (@idlist) { SendSQL("select now()"); $timestamp = FetchOneColumn(); - my $sql_timestamp = SqlQuote($timestamp); + my $sql_timestamp = SqlQuote($timestamp); my $work_time; if (UserInGroup(Param('timetrackinggroup'))) { @@ -1435,9 +1435,9 @@ foreach my $id (@idlist) { } if ($changed) { SendSQL("SELECT keyworddefs.name - FROM keyworddefs, keywords + FROM keyworddefs INNER JOIN keywords + ON keyworddefs.id = keywords.keywordid WHERE keywords.bug_id = $id - AND keyworddefs.id = keywords.keywordid ORDER BY keyworddefs.name"); my @list; while (MoreSQLData()) { diff --git a/request.cgi b/request.cgi index 7f3e6351c..36f6c9ce7 100755 --- a/request.cgi +++ b/request.cgi @@ -92,33 +92,34 @@ sub queue { # so we can display product and component names, and the bug_group_map # and user_group_map tables to help us weed out secure bugs to which # the user should not have access. - " FROM flags - LEFT JOIN attachments ON ($attach_join_clause), - flagtypes, - profiles AS requesters - LEFT JOIN profiles AS requestees - ON flags.requestee_id = requestees.userid, - bugs - LEFT JOIN products ON bugs.product_id = products.id - LEFT JOIN components ON bugs.component_id = components.id - LEFT JOIN bug_group_map AS bgmap - ON bgmap.bug_id = bugs.bug_id - LEFT JOIN user_group_map AS ugmap - ON bgmap.group_id = ugmap.group_id - AND ugmap.user_id = $::userid + " FROM flags + LEFT JOIN attachments + ON ($attach_join_clause) + INNER JOIN flagtypes + ON flags.type_id = flagtypes.id + INNER JOIN profiles AS requesters + ON flags.setter_id = requesters.userid + LEFT JOIN profiles AS requestees + ON flags.requestee_id = requestees.userid + INNER JOIN bugs + ON flags.bug_id = bugs.bug_id + LEFT JOIN products + ON bugs.product_id = products.id + LEFT JOIN components + ON bugs.component_id = components.id + LEFT JOIN bug_group_map AS bgmap + ON bgmap.bug_id = bugs.bug_id + LEFT JOIN user_group_map AS ugmap + ON bgmap.group_id = ugmap.group_id + AND ugmap.user_id = $::userid AND ugmap.isbless = 0 - LEFT JOIN cc AS ccmap - ON ccmap.who = $::userid AND ccmap.bug_id = bugs.bug_id - " . - # All of these are inner join clauses. Actual match criteria are added - # in the code below. - " WHERE flags.type_id = flagtypes.id - AND flags.setter_id = requesters.userid - AND flags.bug_id = bugs.bug_id + LEFT JOIN cc AS ccmap + ON ccmap.who = $::userid + AND ccmap.bug_id = bugs.bug_id "; # Non-deleted flags only - $query .= " AND flags.is_active = 1 "; + $query .= " WHERE flags.is_active = 1 "; # Limit query to pending requests. $query .= " AND flags.status = '?' " unless $cgi->param('status'); diff --git a/sanitycheck.cgi b/sanitycheck.cgi index c05f0e50d..89b657e90 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -546,9 +546,11 @@ if (defined $cgi->param('rebuildkeywordcache')) { } SendSQL("SELECT keywords.bug_id, keyworddefs.name " . - "FROM keywords, keyworddefs, bugs " . - "WHERE keyworddefs.id = keywords.keywordid " . - " AND keywords.bug_id = bugs.bug_id " . + "FROM keywords " . + "INNER JOIN keyworddefs " . + " ON keyworddefs.id = keywords.keywordid " . + "INNER JOIN bugs " . + " ON keywords.bug_id = bugs.bug_id " . "ORDER BY keywords.bug_id, keyworddefs.name"); my $lastb = 0; @@ -629,9 +631,8 @@ sub BugCheck ($$) { Status("Checking resolution/duplicates"); -BugCheck("bugs, duplicates WHERE " . - "bugs.resolution != 'DUPLICATE' AND " . - "bugs.bug_id = duplicates.dupe", +BugCheck("bugs INNER JOIN duplicates ON bugs.bug_id = duplicates.dupe " . + "WHERE bugs.resolution != 'DUPLICATE'", "Bug(s) found on duplicates table that are not marked duplicate"); BugCheck("bugs LEFT JOIN duplicates ON bugs.bug_id = duplicates.dupe WHERE " . @@ -662,10 +663,8 @@ BugCheck("bugs WHERE bug_status IN ('NEW', 'ASSIGNED', 'REOPENED') AND everconfi Status("Checking votes/everconfirmed"); -BugCheck("bugs, products WHERE " . - "bugs.product_id = products.id AND " . - "everconfirmed = 0 AND " . - "votestoconfirm <= votes", +BugCheck("bugs INNER JOIN products ON bugs.product_id = products.id " . + "WHERE everconfirmed = 0 AND votestoconfirm <= votes", "Bugs that have enough votes to be confirmed but haven't been"); ########################################################################### @@ -711,20 +710,26 @@ if ($c) { Status("Checking for bugs with groups violating their product's group controls"); BugCheck("bugs - INNER JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id - INNER JOIN groups ON bug_group_map.group_id = groups.id - LEFT JOIN group_control_map ON bugs.product_id = group_control_map.product_id - AND bug_group_map.group_id = group_control_map.group_id + INNER JOIN bug_group_map + ON bugs.bug_id = bug_group_map.bug_id + INNER JOIN groups + ON bug_group_map.group_id = groups.id + LEFT JOIN group_control_map + ON bugs.product_id = group_control_map.product_id + AND bug_group_map.group_id = group_control_map.group_id WHERE groups.isactive != 0 AND ((group_control_map.membercontrol = " . CONTROLMAPNA . ") OR (group_control_map.membercontrol IS NULL))", "Have groups not permitted for their products"); BugCheck("bugs - INNER JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id - INNER JOIN groups ON bug_group_map.group_id = groups.id - LEFT JOIN group_control_map ON bugs.product_id = group_control_map.product_id - AND bug_group_map.group_id = group_control_map.group_id + INNER JOIN bug_group_map + ON bugs.bug_id = bug_group_map.bug_id + INNER JOIN groups + ON bug_group_map.group_id = groups.id + LEFT JOIN group_control_map + ON bugs.product_id = group_control_map.product_id + AND bug_group_map.group_id = group_control_map.group_id WHERE groups.isactive != 0 AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY . " AND bug_group_map.group_id IS NULL", diff --git a/showdependencytree.cgi b/showdependencytree.cgi index b373563c1..76ef0ddee 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -149,16 +149,17 @@ sub GetBug { my $bug = {}; if (Bugzilla->user->can_see_bug($id)) { SendSQL("SELECT 1, - bug_status, - short_desc, - $milestone_column, - assignee.userid, - assignee.login_name - FROM bugs, profiles AS assignee - WHERE bugs.bug_id = $id - AND bugs.assigned_to = assignee.userid"); - - + bug_status, + short_desc, + $milestone_column, + assignee.userid, + assignee.login_name + FROM bugs + INNER JOIN profiles AS assignee + ON bugs.assigned_to = assignee.userid + WHERE bugs.bug_id = $id"); + + ($bug->{'exists'}, $bug->{'status'}, $bug->{'summary'}, diff --git a/summarize_time.cgi b/summarize_time.cgi index 077678c2e..67bc255e8 100755 --- a/summarize_time.cgi +++ b/summarize_time.cgi @@ -149,11 +149,12 @@ sub include_tt_details { my $q = qq{SELECT bugs.bug_id, profiles.login_name, bugs.deadline, bugs.estimated_time, bugs.remaining_time - FROM longdescs, bugs, profiles - WHERE longdescs.bug_id in ($buglist) AND - longdescs.bug_id = bugs.bug_id AND - longdescs.who = profiles.userid - $date_bits}; + FROM longdescs + INNER JOIN bugs + ON longdescs.bug_id = bugs.bug_id + INNER JOIN profiles + ON longdescs.who = profiles.userid + WHERE longdescs.bug_id in ($buglist) $date_bits}; my %res = %{$res}; my $sth = $dbh->prepare($q); @@ -203,10 +204,10 @@ sub get_blocker_ids_unique { } sub get_blocker_ids_deep { - my ($bug_id, $ret) = @_; + my ($bug_id, $ret) = @_; my $deps = Bugzilla::Bug::EmitDependList("blocked", "dependson", $bug_id); push @{$ret}, @$deps; - foreach $bug_id (@$deps) { + foreach $bug_id (@$deps) { get_blocker_ids_deep($bug_id, $ret); } } @@ -232,10 +233,12 @@ sub query_work_by_buglist { longdescs.bug_id, bugs.short_desc, bugs.bug_status - FROM longdescs, profiles, bugs - WHERE longdescs.bug_id IN ($buglist) AND - longdescs.who = profiles.userid AND - bugs.bug_id = longdescs.bug_id + FROM longdescs + INNER JOIN profiles + ON longdescs.who = profiles.userid + INNER JOIN bugs + ON bugs.bug_id = longdescs.bug_id + WHERE longdescs.bug_id IN ($buglist) $date_bits } . $dbh->sql_group_by('longdescs.bug_id, profiles.login_name', 'bugs.short_desc, bugs.bug_status, longdescs.bug_when') . qq{ @@ -296,9 +299,10 @@ sub get_inactive_bugs { # them in %res here and then remove them below. my $q = qq{SELECT DISTINCT bugs.bug_id, bugs.short_desc , bugs.bug_status - FROM longdescs, bugs - WHERE longdescs.bug_id in ($buglist) AND - longdescs.bug_id = bugs.bug_id}; + FROM longdescs + INNER JOIN bugs + ON longdescs.bug_id = bugs.bug_id + WHERE longdescs.bug_id in ($buglist)}; my $sth = $dbh->prepare($q); $sth->execute(); while (my $row = $sth->fetch) { @@ -312,9 +316,10 @@ sub get_inactive_bugs { longdescs.bug_id, bugs.short_desc, bugs.bug_status - FROM longdescs, bugs - WHERE longdescs.bug_id IN ($buglist) AND - bugs.bug_id = longdescs.bug_id + FROM longdescs + INNER JOIN bugs + ON bugs.bug_id = longdescs.bug_id + WHERE longdescs.bug_id IN ($buglist) $date_bits } . $dbh->sql_group_by('longdescs.bug_id', 'bugs.short_desc, bugs.bug_status') . qq{ diff --git a/userprefs.cgi b/userprefs.cgi index eac0bb108..92e110f3b 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -177,8 +177,9 @@ sub DoEmail { ########################################################################### if (Param("supportwatchers")) { my $watched_ref = $dbh->selectcol_arrayref( - "SELECT profiles.login_name FROM watch, profiles" - . " WHERE watcher = ? AND watch.watched = profiles.userid", + "SELECT profiles.login_name FROM watch INNER JOIN profiles" . + " ON watch.watched = profiles.userid" . + " WHERE watcher = ?", undef, $userid); $vars->{'watchedusers'} = join(',', @$watched_ref); @@ -307,9 +308,10 @@ sub SaveEmail { sub DoPermissions { my (@has_bits, @set_bits); - SendSQL("SELECT DISTINCT name, description FROM groups, user_group_map " . - "WHERE user_group_map.group_id = groups.id " . - "AND user_id = $::userid " . + SendSQL("SELECT DISTINCT name, description FROM groups " . + "INNER JOIN user_group_map " . + "ON user_group_map.group_id = groups.id " . + "WHERE user_id = $::userid " . "AND isbless = 0 " . "ORDER BY name"); while (MoreSQLData()) { diff --git a/votes.cgi b/votes.cgi index 3a22c90b0..128dcba81 100755 --- a/votes.cgi +++ b/votes.cgi @@ -95,9 +95,9 @@ sub show_bug { my @users; SendSQL("SELECT profiles.login_name, votes.who, votes.vote_count - FROM votes, profiles - WHERE votes.bug_id = $bug_id - AND profiles.userid = votes.who"); + FROM votes INNER JOIN profiles + ON profiles.userid = votes.who + WHERE votes.bug_id = $bug_id"); while (MoreSQLData()) { my ($name, $userid, $count) = (FetchSQLData()); @@ -170,10 +170,10 @@ sub show_user { SendSQL("SELECT votes.bug_id, votes.vote_count, bugs.short_desc, bugs.bug_status - FROM votes, bugs, products + FROM votes + INNER JOIN bugs ON votes.bug_id = bugs.bug_id + INNER JOIN products ON bugs.product_id = products.id WHERE votes.who = $who - AND votes.bug_id = bugs.bug_id - AND bugs.product_id = products.id AND products.name = " . SqlQuote($product) . "ORDER BY votes.bug_id"); @@ -280,9 +280,10 @@ sub record_votes { # the ballot box. if (scalar(@buglist)) { SendSQL("SELECT bugs.bug_id, products.name, products.maxvotesperbug - FROM bugs, products - WHERE products.id = bugs.product_id - AND bugs.bug_id IN (" . join(", ", @buglist) . ")"); + FROM bugs + INNER JOIN products + ON products.id = bugs.product_id + WHERE bugs.bug_id IN (" . join(", ", @buglist) . ")"); my %prodcount; diff --git a/whineatnews.pl b/whineatnews.pl index d90e775fa..286b0c542 100755 --- a/whineatnews.pl +++ b/whineatnews.pl @@ -34,11 +34,13 @@ require "globals.pl"; use Bugzilla::BugMail; my $dbh = Bugzilla->dbh; -SendSQL("SELECT bug_id, short_desc, login_name FROM bugs, profiles WHERE " . - "(bug_status = 'NEW' OR bug_status = 'REOPENED') AND " . - $dbh->sql_to_days('NOW()') . " - " . - $dbh->sql_to_days('delta_ts') . " > " . Param('whinedays') . - " AND userid = assigned_to ORDER BY bug_id"); +SendSQL("SELECT bug_id, short_desc, login_name " . + "FROM bugs INNER JOIN profiles ON userid = assigned_to " . + "WHERE (bug_status = 'NEW' OR bug_status = 'REOPENED') " . + "AND " . $dbh->sql_to_days('NOW()') . " - " . + $dbh->sql_to_days('delta_ts') . " > " . + Param('whinedays') . " " . + "ORDER BY bug_id"); my %bugs; my %desc; -- cgit v1.2.3-65-gdbad From 6f31547ef949153e4ee591b28eec95dcf8bd5525 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Tue, 5 Apr 2005 05:23:36 +0000 Subject: Bug 288405: Attachment.cgi fails on Big File attachment inserts - Patch by Marc Schumann r=joel a=myk --- attachment.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index fadbe8b97..df25b768d 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -397,7 +397,7 @@ sub validateData } } - return $data; + return $data || ''; } my $filename; -- cgit v1.2.3-65-gdbad From 0ca4c4c49583b2dc680ace8ac9b7c80ea62d7f37 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Fri, 8 Apr 2005 06:37:35 +0000 Subject: Bug 238878: Make hidden-fields template, User Matching and Flags use direct CGI instead of [% form.foo %] - Patch by Teemu Mannermaa r=LpSolit a=justdave --- Bugzilla/Flag.pm | 50 +++++++++---------- Bugzilla/FlagType.pm | 14 +++--- Bugzilla/User.pm | 58 ++++++++++++---------- attachment.cgi | 22 ++++---- editwhines.cgi | 2 +- post_bug.cgi | 2 +- process_bug.cgi | 9 ++-- .../en/default/global/confirm-user-match.html.tmpl | 2 - template/en/default/global/hidden-fields.html.tmpl | 23 ++++----- 9 files changed, 93 insertions(+), 89 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 961957278..0aca49c87 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -228,7 +228,7 @@ sub count { =over -=item C +=item C Validates fields containing flag modifications. @@ -238,17 +238,17 @@ Validates fields containing flag modifications. sub validate { my $user = Bugzilla->user; - my ($data, $bug_id) = @_; + my ($cgi, $bug_id) = @_; # Get a list of flags to validate. Uses the "map" function # to extract flag IDs from form field names by matching fields # whose name looks like "flag-nnn", where "nnn" is the ID, # and returning just the ID portion of matching field names. - my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data); + my @ids = map(/^flag-(\d+)$/ ? $1 : (), $cgi->param()); foreach my $id (@ids) { - my $status = $data->{"flag-$id"}; + my $status = $cgi->param("flag-$id"); # Make sure the flag exists. my $flag = get($id); @@ -277,9 +277,9 @@ sub validate { # feature and the attachment is marked private). if ($status eq '?' && $flag->{type}->{is_requesteeble} - && trim($data->{"requestee-$id"})) + && trim($cgi->param("requestee-$id"))) { - my $requestee_email = trim($data->{"requestee-$id"}); + my $requestee_email = trim($cgi->param("requestee-$id")); if ($requestee_email ne $flag->{'requestee'}->{'email'}) { # We know the requestee exists because we ran # Bugzilla::User::match_field before getting here. @@ -299,7 +299,7 @@ sub validate { # Throw an error if the target is a private attachment and # the requestee isn't in the group of insiders who can see it. if ($flag->{target}->{attachment}->{exists} - && $data->{'isprivate'} + && $cgi->param('isprivate') && Param("insidergroup") && !$requestee->in_group(Param("insidergroup"))) { @@ -353,21 +353,21 @@ sub snapshot { =over -=item C +=item C Processes changes to flags. The target is the bug or attachment this flag is about, the timestamp is the date/time the bug was last touched (so that changes to the flag -can be stamped with the same date/time), the data is the form data -with flag fields that the user submitted. +can be stamped with the same date/time), the cgi is the CGI object +used to obtain the flag fields that the user submitted. =back =cut sub process { - my ($target, $timestamp, $data) = @_; + my ($target, $timestamp, $cgi) = @_; my $dbh = Bugzilla->dbh; my $bug_id = $target->{'bug'}->{'id'}; @@ -381,14 +381,14 @@ sub process { my @old_summaries = snapshot($bug_id, $attach_id); # Cancel pending requests if we are obsoleting an attachment. - if ($attach_id && $data->{'isobsolete'}) { + if ($attach_id && $cgi->param('isobsolete')) { CancelRequests($bug_id, $attach_id); } # Create new flags and update existing flags. - my $new_flags = FormToNewFlags($target, $data); + my $new_flags = FormToNewFlags($target, $cgi); foreach my $flag (@$new_flags) { create($flag, $timestamp) } - modify($data, $timestamp); + modify($cgi, $timestamp); # In case the bug's product/component has changed, clear flags that are # no longer valid. @@ -521,7 +521,7 @@ sub migrate { =over -=item C +=item C Modifies flags in the database when a user changes them. Note that modified flags are always set active (is_active = 1) - @@ -533,14 +533,14 @@ attachment.cgi midairs. See bug 223878 for details. =cut sub modify { - my ($data, $timestamp) = @_; + my ($cgi, $timestamp) = @_; # Use the date/time we were given if possible (allowing calling code # to synchronize the comment's timestamp with those of other records). $timestamp = ($timestamp ? &::SqlQuote($timestamp) : "NOW()"); # Extract a list of flags from the form data. - my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data); + my @ids = map(/^flag-(\d+)$/ ? $1 : (), $cgi->param()); # Loop over flags and update their record in the database if necessary. # Two kinds of changes can happen to a flag: it can be set to a different @@ -550,8 +550,8 @@ sub modify { foreach my $id (@ids) { my $flag = get($id); - my $status = $data->{"flag-$id"}; - my $requestee_email = trim($data->{"requestee-$id"}); + my $status = $cgi->param("flag-$id"); + my $requestee_email = trim($cgi->param("requestee-$id")); # Ignore flags the user didn't change. There are two components here: @@ -672,7 +672,7 @@ sub clear { =over -=item C{"flag_type-$_"} ne 'X', @type_ids); + my @type_ids = map(/^flag_type-(\d+)$/ ? $1 : (), $cgi->param()); + @type_ids = grep($cgi->param("flag_type-$_") ne 'X', @type_ids); # Process the form data and create an array of flag objects. my @flags; foreach my $type_id (@type_ids) { - my $status = $data->{"flag_type-$type_id"}; + my $status = $cgi->param("flag_type-$type_id"); &::trick_taint($status); # Create the flag record and populate it with data from the form. @@ -706,7 +706,7 @@ sub FormToNewFlags { }; if ($status eq "?") { - my $requestee = $data->{"requestee_type-$type_id"}; + my $requestee = $cgi->param("requestee_type-$type_id"); if ($requestee) { my $requestee_id = login_to_id($requestee); $flag->{'requestee'} = new Bugzilla::User($requestee_id); diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index 084777b29..da0c43281 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -303,7 +303,7 @@ sub count { =over -=item C +=item C Get a list of flag types to validate. Uses the "map" function to extract flag type IDs from form field names by matching columns @@ -316,13 +316,13 @@ and returning just the ID portion of matching field names. sub validate { my $user = Bugzilla->user; - my ($data, $bug_id, $attach_id) = @_; + my ($cgi, $bug_id, $attach_id) = @_; - my @ids = map(/^flag_type-(\d+)$/ ? $1 : (), keys %$data); + my @ids = map(/^flag_type-(\d+)$/ ? $1 : (), $cgi->param()); foreach my $id (@ids) { - my $status = $data->{"flag_type-$id"}; + my $status = $cgi->param("flag_type-$id"); # Don't bother validating types the user didn't touch. next if $status eq "X"; @@ -348,9 +348,9 @@ sub validate { # feature and the attachment is marked private). if ($status eq '?' && $flag_type->{is_requesteeble} - && trim($data->{"requestee_type-$id"})) + && trim($cgi->param("requestee_type-$id"))) { - my $requestee_email = trim($data->{"requestee_type-$id"}); + my $requestee_email = trim($cgi->param("requestee_type-$id")); # We know the requestee exists because we ran # Bugzilla::User::match_field before getting here. @@ -370,7 +370,7 @@ sub validate { # the requestee isn't in the group of insiders who can see it. if ($attach_id && Param("insidergroup") - && $data->{'isprivate'} + && $cgi->param('isprivate') && !$requestee->in_group(Param("insidergroup"))) { ThrowUserError("flag_requestee_unauthorized_attachment", diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 2096db407..8cb4d5f99 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -687,7 +687,8 @@ sub match { # Here's what it does: # # 1. Accepts a list of fields along with whether they may take multiple values -# 2. Takes the values of those fields from $::FORM and passes them to match() +# 2. Takes the values of those fields from the first parameter, a $cgi object +# and passes them to match() # 3. Checks the results of the match and displays confirmation or failure # messages as appropriate. # @@ -710,7 +711,7 @@ sub match { # How to call it: # -# Bugzilla::User::match_field ({ +# Bugzilla::User::match_field($cgi, { # 'field_name' => { 'type' => fieldtype }, # 'field_name2' => { 'type' => fieldtype }, # [...] @@ -720,8 +721,8 @@ sub match { # sub match_field { - - my $fields = shift; # arguments as a hash + my $cgi = shift; # CGI object to look up fields in + my $fields = shift; # arguments as a hash my $matches = {}; # the values sent to the template my $matchsuccess = 1; # did the match fail? my $need_confirm = 0; # whether to display confirmation screen @@ -729,11 +730,9 @@ sub match_field { # prepare default form values my $vars = $::vars; - $vars->{'form'} = \%::FORM; - $vars->{'mform'} = \%::MFORM; # What does a "--do_not_change--" field look like (if any)? - my $dontchange = $vars->{'form'}->{'dontchange'}; + my $dontchange = $cgi->param('dontchange'); # Fields can be regular expressions matching multiple form fields # (f.e. "requestee-(\d+)"), so expand each non-literal field @@ -747,7 +746,7 @@ sub match_field { $expanded_fields->{$field_pattern} = $fields->{$field_pattern}; } else { - my @field_names = grep(/$field_pattern/, keys %{$vars->{'form'}}); + my @field_names = grep(/$field_pattern/, $cgi->param()); foreach my $field_name (@field_names) { $expanded_fields->{$field_name} = { type => $fields->{$field_pattern}->{'type'} }; @@ -774,23 +773,27 @@ sub match_field { # Tolerate fields that do not exist. # # This is so that fields like qa_contact can be specified in the code - # and it won't break if $::MFORM does not define them. + # and it won't break if the CGI object does not know about them. # # It has the side-effect that if a bad field name is passed it will be # quietly ignored rather than raising a code error. - next if !defined($vars->{'mform'}->{$field}); + next if !defined $cgi->param($field); # Skip it if this is a --do_not_change-- field - next if $dontchange && $dontchange eq $vars->{'form'}->{$field}; + next if $dontchange && $dontchange eq $cgi->param($field); # We need to move the query to $raw_field, where it will be split up, - # modified by the search, and put back into $::FORM and $::MFORM + # modified by the search, and put back into the CGI environment # incrementally. - my $raw_field = join(" ", @{$vars->{'mform'}->{$field}}); - $vars->{'form'}->{$field} = ''; - $vars->{'mform'}->{$field} = []; + my $raw_field = join(" ", $cgi->param($field)); + + # When we add back in values later, it matters that we delete + # the param here, and not set it to '', so that we will add + # things to an empty list, and not to a list containing one + # empty string + $cgi->delete($field); my @queries = (); @@ -835,12 +838,12 @@ sub match_field { if ((scalar(@{$users}) == 1) && (@{$users}[0]->{'login'} eq $query)) { - # delimit with spaces if necessary - if ($vars->{'form'}->{$field}) { - $vars->{'form'}->{$field} .= " "; - } - $vars->{'form'}->{$field} .= @{$users}[0]->{'login'}; - push @{$vars->{'mform'}->{$field}}, @{$users}[0]->{'login'}; + $cgi->append(-name=>$field, + -values=>[@{$users}[0]->{'login'}]); + + # XXX FORM compatilibity code, will be removed in bug 225818 + $::FORM{$field} = join(" ", $cgi->param($field)); + next; } @@ -850,12 +853,13 @@ sub match_field { # here is where it checks for multiple matches if (scalar(@{$users}) == 1) { # exactly one match - # delimit with spaces if necessary - if ($vars->{'form'}->{$field}) { - $vars->{'form'}->{$field} .= " "; - } - $vars->{'form'}->{$field} .= @{$users}[0]->{'login'}; - push @{$vars->{'mform'}->{$field}}, @{$users}[0]->{'login'}; + + $cgi->append(-name=>$field, + -values=>[@{$users}[0]->{'login'}]); + + # XXX FORM compatilibity code, will be removed in bug 225818 + $::FORM{$field} = join(" ", $cgi->param($field)); + $need_confirm = 1 if &::Param('confirmuniqueusermatch'); } diff --git a/attachment.cgi b/attachment.cgi index df25b768d..9f4b603dc 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -126,10 +126,11 @@ elsif ($action eq "insert") # The order of these function calls is important, as both Flag::validate # and FlagType::validate assume User::match_field has ensured that the values # in the requestee fields are legitimate user email addresses. - Bugzilla::User::match_field({ '^requestee(_type)?-(\d+)$' => - { 'type' => 'single' } }); - Bugzilla::Flag::validate(\%::FORM, $bugid); - Bugzilla::FlagType::validate(\%::FORM, $bugid, $::FORM{'id'}); + Bugzilla::User::match_field($cgi, { + '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } + }); + Bugzilla::Flag::validate($cgi, $bugid); + Bugzilla::FlagType::validate($cgi, $bugid, $::FORM{'id'}); insert($data); } @@ -155,10 +156,11 @@ elsif ($action eq "update") # The order of these function calls is important, as both Flag::validate # and FlagType::validate assume User::match_field has ensured that the values # in the requestee fields are legitimate user email addresses. - Bugzilla::User::match_field({ '^requestee(_type)?-(\d+)$' => - { 'type' => 'single' } }); - Bugzilla::Flag::validate(\%::FORM, $bugid); - Bugzilla::FlagType::validate(\%::FORM, $bugid, $::FORM{'id'}); + Bugzilla::User::match_field($cgi, { + '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } + }); + Bugzilla::Flag::validate($cgi, $bugid); + Bugzilla::FlagType::validate($cgi, $bugid, $::FORM{'id'}); update(); } @@ -1033,7 +1035,7 @@ sub insert # Create flags. my $target = Bugzilla::Flag::GetTarget(undef, $attachid); - Bugzilla::Flag::process($target, $timestamp, \%::FORM); + Bugzilla::Flag::process($target, $timestamp, $cgi); # Define the variables and functions that will be passed to the UI template. $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login, @@ -1168,7 +1170,7 @@ sub update # is obsoleting this attachment without deleting any requests # the user submits at the same time. my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); - Bugzilla::Flag::process($target, $timestamp, \%::FORM); + Bugzilla::Flag::process($target, $timestamp, $cgi); # Update the attachment record in the database. SendSQL("UPDATE attachments diff --git a/editwhines.cgi b/editwhines.cgi index 013ee0df4..e65a585d0 100755 --- a/editwhines.cgi +++ b/editwhines.cgi @@ -198,7 +198,7 @@ if ($cgi->param('update')) { } } if (scalar %{$arglist}) { - &Bugzilla::User::match_field($arglist); + &Bugzilla::User::match_field($cgi, $arglist); } for my $sid (@scheduleids) { diff --git a/post_bug.cgi b/post_bug.cgi index 7f05dc7bc..84a9fd9df 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -60,7 +60,7 @@ my $dbh = Bugzilla->dbh; # do a match on the fields if applicable -&Bugzilla::User::match_field ({ +&Bugzilla::User::match_field ($cgi, { 'cc' => { 'type' => 'multi' }, 'assigned_to' => { 'type' => 'single' }, 'qa_contact' => { 'type' => 'single' }, diff --git a/process_bug.cgi b/process_bug.cgi index 94d86c936..aff3698bd 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -44,6 +44,7 @@ use Bugzilla::Util; # Use the Flag module to modify flag data if the user set flags. use Bugzilla::Flag; +use Bugzilla::FlagType; # Shut up misguided -w warnings about "used only once": @@ -138,7 +139,7 @@ foreach my $field ("dependson", "blocked") { # The order of these function calls is important, as both Flag::validate # and FlagType::validate assume User::match_field has ensured that the values # in the requestee fields are legitimate user email addresses. -&Bugzilla::User::match_field({ +&Bugzilla::User::match_field($cgi, { 'qa_contact' => { 'type' => 'single' }, 'newcc' => { 'type' => 'multi' }, 'masscc' => { 'type' => 'multi' }, @@ -148,8 +149,8 @@ foreach my $field ("dependson", "blocked") { # Validate flags, but only if the user is changing a single bug, # since the multi-change form doesn't include flag changes. if (defined $::FORM{'id'}) { - Bugzilla::Flag::validate(\%::FORM, $::FORM{'id'}); - Bugzilla::FlagType::validate(\%::FORM, $::FORM{'id'}); + Bugzilla::Flag::validate($cgi, $::FORM{'id'}); + Bugzilla::FlagType::validate($cgi, $::FORM{'id'}); } ###################################################################### @@ -1814,7 +1815,7 @@ foreach my $id (@idlist) { # Set and update flags. if ($UserInEditGroupSet) { my $target = Bugzilla::Flag::GetTarget($id); - Bugzilla::Flag::process($target, $timestamp, \%::FORM); + Bugzilla::Flag::process($target, $timestamp, $cgi); } if ($bug_changed) { SendSQL("UPDATE bugs SET delta_ts = $sql_timestamp WHERE bug_id = $id"); diff --git a/template/en/default/global/confirm-user-match.html.tmpl b/template/en/default/global/confirm-user-match.html.tmpl index 456af1fc2..fd09f89a6 100644 --- a/template/en/default/global/confirm-user-match.html.tmpl +++ b/template/en/default/global/confirm-user-match.html.tmpl @@ -21,8 +21,6 @@ #%] [%# INTERFACE: - # form: hash; the form values submitted to the script - # mform: hash; the form multi-values submitted to the script # fields: hash/record; the fields being matched, each of which has: # type: single|multi: whether or not the user can select multiple matches # flag_type: for flag requestee fields, the type of flag being requested diff --git a/template/en/default/global/hidden-fields.html.tmpl b/template/en/default/global/hidden-fields.html.tmpl index 2fa577b43..7327e43cf 100644 --- a/template/en/default/global/hidden-fields.html.tmpl +++ b/template/en/default/global/hidden-fields.html.tmpl @@ -20,23 +20,22 @@ #%] [%# INTERFACE: - # form: hash; the form fields/values for which to generate hidden fields. - # mform: hash; the form fields/values with multiple values for which to - # generate hidden fields. # exclude: string; a regular expression matching fields to exclude # from the list of hidden fields generated by this template #%] +[%# The global Bugzilla->cgi object is used to obtain form variable values. %] +[% USE Bugzilla %] +[% cgi = Bugzilla.cgi %] + [%# Generate hidden form fields for non-excluded fields. %] -[% FOREACH field = form %] - [% NEXT IF exclude && field.key.search(exclude) %] - [% IF mform.${field.key}.size > 1 %] - [% FOREACH mvalue = mform.${field.key} %] - [% END %] - [% ELSE %] - - [% END %] [% END %] -- cgit v1.2.3-65-gdbad From 7c03ca4cc9a029997a4041c2c5b5eb91d4d2d0b8 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Fri, 8 Apr 2005 07:34:39 +0000 Subject: Bug 238870: remove %FORM from attachment.cgi - Patch by Teemu Mannermaa r=myk,LpSolit a=justdave --- attachment.cgi | 546 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 287 insertions(+), 259 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index 9f4b603dc..2b119e7ff 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -50,20 +50,8 @@ use Bugzilla::User; use Bugzilla::Util; use Bugzilla::Bug; -# Check whether or not the user is logged in and, if so, set the $::userid Bugzilla->login(); -# The ID of the bug to which the attachment is attached. Gets set -# by validateID() (which validates the attachment ID, not the bug ID, but has -# to check if the user is authorized to access this attachment) and is used -# by Flag:: and FlagType::validate() to ensure the requestee (if any) for a -# requested flag is authorized to see the bug in question. Note: This should -# really be defined just above validateID() itself, but it's used in the main -# body of the script before that function is defined, so we define it up here -# instead. We should move the validation into each function and then move this -# to just above validateID(). -my $bugid; - my $cgi = Bugzilla->cgi; ################################################################################ @@ -76,93 +64,42 @@ my $cgi = Bugzilla->cgi; # supplied, we default to 'view'. # Determine whether to use the action specified by the user or the default. -my $action = $::FORM{'action'} || 'view'; +my $action = $cgi->param('action') || 'view'; if ($action eq "view") { - validateID(); - view(); + view(); } elsif ($action eq "interdiff") { - validateID('oldid'); - validateID('newid'); - validateFormat("html", "raw"); - validateContext(); - interdiff(); + interdiff(); } elsif ($action eq "diff") { - validateID(); - validateFormat("html", "raw"); - validateContext(); - diff(); + diff(); } elsif ($action eq "viewall") { - ValidateBugID($::FORM{'bugid'}); - viewall(); + viewall(); } elsif ($action eq "enter") { - Bugzilla->login(LOGIN_REQUIRED); - ValidateBugID($::FORM{'bugid'}); - validateCanChangeBug($::FORM{'bugid'}); - enter(); + Bugzilla->login(LOGIN_REQUIRED); + enter(); } elsif ($action eq "insert") { - Bugzilla->login(LOGIN_REQUIRED); - ValidateBugID($::FORM{'bugid'}); - validateCanChangeBug($::FORM{'bugid'}); - ValidateComment($::FORM{'comment'}); - validateFilename(); - validateIsPatch(); - my $data = validateData(); - validateDescription(); - validateContentType() unless $::FORM{'ispatch'}; - validateObsolete() if $::FORM{'obsolete'}; - - # The order of these function calls is important, as both Flag::validate - # and FlagType::validate assume User::match_field has ensured that the values - # in the requestee fields are legitimate user email addresses. - Bugzilla::User::match_field($cgi, { - '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } - }); - Bugzilla::Flag::validate($cgi, $bugid); - Bugzilla::FlagType::validate($cgi, $bugid, $::FORM{'id'}); - - insert($data); + Bugzilla->login(LOGIN_REQUIRED); + insert(); } elsif ($action eq "edit") { - validateID(); - validateCanEdit($::FORM{'id'}); - edit(); + edit(); } elsif ($action eq "update") { - Bugzilla->login(LOGIN_REQUIRED); - ValidateComment($::FORM{'comment'}); - validateID(); - validateCanEdit($::FORM{'id'}); - validateCanChangeAttachment($::FORM{'id'}); - validateDescription(); - validateIsPatch(); - validateContentType() unless $::FORM{'ispatch'}; - validateIsObsolete(); - validatePrivate(); - - # The order of these function calls is important, as both Flag::validate - # and FlagType::validate assume User::match_field has ensured that the values - # in the requestee fields are legitimate user email addresses. - Bugzilla::User::match_field($cgi, { - '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } - }); - Bugzilla::Flag::validate($cgi, $bugid); - Bugzilla::FlagType::validate($cgi, $bugid, $::FORM{'id'}); - - update(); + Bugzilla->login(LOGIN_REQUIRED); + update(); } else { @@ -175,6 +112,18 @@ exit; # Data Validation / Security Authorization ################################################################################ +# Validates an attachment ID. Optionally takes a parameter of a form +# variable name that contains the ID to be validated. If not specified, +# uses 'id'. +# +# Will throw an error if 1) attachment ID is not a valid number, +# 2) attachment does not exist, or 3) user isn't allowed to access the +# attachment. +# +# Returns a list, where the first item is the validated, detainted +# attachment id, and the 2nd item is the bug id corresponding to the +# attachment. +# sub validateID { my $param = @_ ? $_[0] : 'id'; @@ -204,7 +153,8 @@ sub validateID || ThrowUserError("invalid_attach_id", { attach_id => $attach_id }); # Make sure the user is authorized to access this attachment's bug. - ($bugid, my $isprivate) = FetchSQLData(); + (my $bugid, my $isprivate) = FetchSQLData(); + ValidateBugID($bugid); if ($isprivate && Param("insidergroup")) { UserInGroup(Param("insidergroup")) @@ -212,10 +162,12 @@ sub validateID object => "attachment"}); } - # XXX shim code, kill $::FORM - $::FORM{$param} = $attach_id; + return ($attach_id,$bugid); } +# Validates format of a diff/interdiff. Takes a list as an parameter, which +# defines the valid format values. Will throw an error if the format is not +# in the list. Returns either the user selected or default format. sub validateFormat { # receives a list of legal formats; first item is a default @@ -225,10 +177,11 @@ sub validateFormat ThrowUserError("invalid_format", { format => $format, formats => \@_ }); } - # XXX shim code, kill $::FORM - $::FORM{'format'} = $format; + return $format; } +# Validates context of a diff/interdiff. Will throw an error if the context +# is not number, "file" or "patch". Returns the validated, detainted context. sub validateContext { my $context = $cgi->param('context') || "patch"; @@ -236,8 +189,8 @@ sub validateContext detaint_natural($context) || ThrowUserError("invalid_context", { context => $cgi->param('context') }); } - # XXX shim code, kill $::FORM - $::FORM{'context'} = $context; + + return $context; } sub validateCanEdit @@ -249,14 +202,14 @@ sub validateCanEdit # People not logged in can't actually commit changes, because that code # calls Bugzilla->login with LOGIN_REQUIRED, not with LOGIN_NORMAL, # before calling this sub - return if $::userid == 0; + return unless Bugzilla->user; # People in editbugs can edit all attachments return if UserInGroup("editbugs"); # Bug 97729 - the submitter can edit their attachments SendSQL("SELECT attach_id FROM attachments WHERE " . - "attach_id = $attach_id AND submitter_id = $::userid"); + "attach_id = $attach_id AND submitter_id = " . Bugzilla->user->id); FetchSQLData() || ThrowUserError("illegal_attachment_edit", @@ -291,28 +244,28 @@ sub validateCanChangeBug sub validateDescription { - $::FORM{'description'} - || ThrowUserError("missing_attachment_description"); + $cgi->param('description') + || ThrowUserError("missing_attachment_description"); } sub validateIsPatch { - # Set the ispatch flag to zero if it is undefined, since the UI uses - # an HTML checkbox to represent this flag, and unchecked HTML checkboxes - # do not get sent in HTML requests. - $::FORM{'ispatch'} = $::FORM{'ispatch'} ? 1 : 0; + # Set the ispatch flag to zero if it is undefined, since the UI uses + # an HTML checkbox to represent this flag, and unchecked HTML checkboxes + # do not get sent in HTML requests. + $cgi->param('ispatch', $cgi->param('ispatch') ? 1 : 0); - # Set the content type to text/plain if the attachment is a patch. - $::FORM{'contenttype'} = "text/plain" if $::FORM{'ispatch'}; + # Set the content type to text/plain if the attachment is a patch. + $cgi->param('contenttype', 'text/plain') if $cgi->param('ispatch'); } sub validateContentType { - if (!$::FORM{'contenttypemethod'}) + if (!defined $cgi->param('contenttypemethod')) { ThrowUserError("missing_content_type_method"); } - elsif ($::FORM{'contenttypemethod'} eq 'autodetect') + elsif ($cgi->param('contenttypemethod') eq 'autodetect') { my $contenttype = $cgi->uploadInfo($cgi->param('data'))->{'Content-Type'}; # The user asked us to auto-detect the content type, so use the type @@ -321,37 +274,38 @@ sub validateContentType { ThrowUserError("missing_content_type"); } - $::FORM{'contenttype'} = $contenttype; + $cgi->param('contenttype', $contenttype); } - elsif ($::FORM{'contenttypemethod'} eq 'list') + elsif ($cgi->param('contenttypemethod') eq 'list') { # The user selected a content type from the list, so use their selection. - $::FORM{'contenttype'} = $::FORM{'contenttypeselection'}; + $cgi->param('contenttype', $cgi->param('contenttypeselection')); } - elsif ($::FORM{'contenttypemethod'} eq 'manual') + elsif ($cgi->param('contenttypemethod') eq 'manual') { # The user entered a content type manually, so use their entry. - $::FORM{'contenttype'} = $::FORM{'contenttypeentry'}; + $cgi->param('contenttype', $cgi->param('contenttypeentry')); } else { ThrowCodeError("illegal_content_type_method", - { contenttypemethod => $::FORM{'contenttypemethod'} }); + { contenttypemethod => $cgi->param('contenttypemethod') }); } - if ( $::FORM{'contenttype'} !~ /^(application|audio|image|message|model|multipart|text|video)\/.+$/ ) + if ( $cgi->param('contenttype') !~ + /^(application|audio|image|message|model|multipart|text|video)\/.+$/ ) { ThrowUserError("invalid_content_type", - { contenttype => $::FORM{'contenttype'} }); + { contenttype => $cgi->param('contenttype') }); } } sub validateIsObsolete { - # Set the isobsolete flag to zero if it is undefined, since the UI uses - # an HTML checkbox to represent this flag, and unchecked HTML checkboxes - # do not get sent in HTML requests. - $::FORM{'isobsolete'} = $::FORM{'isobsolete'} ? 1 : 0; + # Set the isobsolete flag to zero if it is undefined, since the UI uses + # an HTML checkbox to represent this flag, and unchecked HTML checkboxes + # do not get sent in HTML requests. + $cgi->param('isobsolete', $cgi->param('isobsolete') ? 1 : 0); } sub validatePrivate @@ -359,17 +313,17 @@ sub validatePrivate # Set the isprivate flag to zero if it is undefined, since the UI uses # an HTML checkbox to represent this flag, and unchecked HTML checkboxes # do not get sent in HTML requests. - $::FORM{'isprivate'} = $::FORM{'isprivate'} ? 1 : 0; + $cgi->param('isprivate', $cgi->param('isprivate') ? 1 : 0); } sub validateData { - my $maxsize = $::FORM{'ispatch'} ? Param('maxpatchsize') : Param('maxattachmentsize'); + my $maxsize = $cgi->param('ispatch') ? Param('maxpatchsize') : Param('maxattachmentsize'); $maxsize *= 1024; # Convert from K my $fh; # Skip uploading into a local variable if the user wants to upload huge # attachments into local files. - if (!$::FORM{'bigfile'}) + if (!$cgi->param('bigfile')) { $fh = $cgi->upload('data'); } @@ -377,7 +331,7 @@ sub validateData # We could get away with reading only as much as required, except that then # we wouldn't have a size to print to the error handler below. - if (!$::FORM{'bigfile'}) + if (!$cgi->param('bigfile')) { # enable 'slurp' mode local $/; @@ -385,14 +339,14 @@ sub validateData } $data - || ($::FORM{'bigfile'}) + || ($cgi->param('bigfile')) || ThrowUserError("zero_length_file"); # Make sure the attachment does not exceed the maximum permitted size my $len = $data ? length($data) : 0; if ($maxsize && $len > $maxsize) { my $vars = { filesize => sprintf("%.0f", $len/1024) }; - if ( $::FORM{'ispatch'} ) { + if ($cgi->param('ispatch')) { ThrowUserError("patch_too_large", $vars); } else { ThrowUserError("file_too_large", $vars); @@ -402,13 +356,12 @@ sub validateData return $data || ''; } -my $filename; sub validateFilename { defined $cgi->upload('data') || ThrowUserError("file_not_specified"); - $filename = $cgi->upload('data'); + my $filename = $cgi->upload('data'); # Remove path info (if any) from the file name. The browser should do this # for us, but some are buggy. This may not work on Mac file names and could @@ -420,13 +373,17 @@ sub validateFilename # Truncate the filename to 100 characters, counting from the end of the string # to make sure we keep the filename extension. $filename = substr($filename, -100, 100); + + return $filename; } sub validateObsolete { + my @obsolete_ids = (); + # Make sure the attachment id is valid and the user has permissions to view # the bug to which it is attached. - foreach my $attachid (@{$::MFORM{'obsolete'}}) { + foreach my $attachid ($cgi->param('obsolete')) { my $vars = {}; $vars->{'attach_id'} = $attachid; @@ -444,9 +401,9 @@ sub validateObsolete $vars->{'description'} = $description; - if ($bugid != $::FORM{'bugid'}) + if ($bugid != $cgi->param('bugid')) { - $vars->{'my_bug_id'} = $::FORM{'bugid'}; + $vars->{'my_bug_id'} = $cgi->param('bugid'); $vars->{'attach_bug_id'} = $bugid; ThrowCodeError("mismatched_bug_ids_on_obsolete", $vars); } @@ -458,7 +415,10 @@ sub validateObsolete # Check that the user can modify this attachment validateCanEdit($attachid); + push(@obsolete_ids, $attachid); } + + return @obsolete_ids; } # Returns 1 if the parameter is a content-type viewable in this browser @@ -497,21 +457,25 @@ sub isViewable # Functions ################################################################################ +# Display an attachment. sub view { - # Display an attachment. + # Retrieve and validate parameters + my ($attach_id) = validateID(); # Retrieve the attachment content and its content type from the database. - SendSQL("SELECT mimetype, filename, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); + SendSQL("SELECT mimetype, filename, thedata FROM attachments " . + "WHERE attach_id = $attach_id"); my ($contenttype, $filename, $thedata) = FetchSQLData(); - # Bug 111522: allow overriding content-type manually in the posted $::FORM. - if ($::FORM{'content_type'}) + # Bug 111522: allow overriding content-type manually in the posted form + # params. + if (defined $cgi->param('content_type')) { - $::FORM{'contenttypemethod'} = 'manual'; - $::FORM{'contenttypeentry'} = $::FORM{'content_type'}; + $cgi->param('contenttypemethod', 'manual'); + $cgi->param('contenttypeentry', $cgi->param('content_type')); validateContentType(); - $contenttype = $::FORM{'content_type'}; + $contenttype = $cgi->param('content_type'); } # Return the appropriate HTTP response headers. @@ -521,10 +485,9 @@ sub view # stored in a local file if ($filesize == 0) { - my $attachid = $::FORM{'id'}; - my $hash = ($attachid % 100) + 100; + my $hash = ($attach_id % 100) + 100; $hash =~ s/.*(\d\d)$/group.$1/; - if (open(AH, "$attachdir/$hash/attachment.$attachid")) { + if (open(AH, "$attachdir/$hash/attachment.$attach_id")) { binmode AH; $filesize = (stat(AH))[7]; } @@ -556,13 +519,19 @@ sub view sub interdiff { + # Retrieve and validate parameters + my ($old_id) = validateID('oldid'); + my ($new_id) = validateID('newid'); + my $format = validateFormat('html', 'raw'); + my $context = validateContext(); + # Get old patch data my ($old_bugid, $old_description, $old_filename, $old_file_list) = - get_unified_diff($::FORM{'oldid'}); + get_unified_diff($old_id); # Get new patch data my ($new_bugid, $new_description, $new_filename, $new_file_list) = - get_unified_diff($::FORM{'newid'}); + get_unified_diff($new_id); my $warning = warn_if_interdiff_might_fail($old_file_list, $new_file_list); @@ -574,8 +543,8 @@ sub interdiff $ENV{'PATH'} = $::diffpath; open my $interdiff_fh, "$::interdiffbin $old_filename $new_filename|"; binmode $interdiff_fh; - my ($reader, $last_reader) = setup_patch_readers(""); - if ($::FORM{'format'} eq "raw") + my ($reader, $last_reader) = setup_patch_readers("", $context); + if ($format eq 'raw') { require PatchReader::DiffPrinter::raw; $last_reader->sends_data_to(new PatchReader::DiffPrinter::raw()); @@ -587,16 +556,16 @@ sub interdiff { $vars->{warning} = $warning if $warning; $vars->{bugid} = $new_bugid; - $vars->{oldid} = $::FORM{'oldid'}; + $vars->{oldid} = $old_id; $vars->{old_desc} = $old_description; - $vars->{newid} = $::FORM{'newid'}; + $vars->{newid} = $new_id; $vars->{new_desc} = $new_description; delete $vars->{attachid}; delete $vars->{do_context}; delete $vars->{context}; - setup_template_patch_reader($last_reader); + setup_template_patch_reader($last_reader, $format, $context); } - $reader->iterate_fh($interdiff_fh, "interdiff #$::FORM{'oldid'} #$::FORM{'newid'}"); + $reader->iterate_fh($interdiff_fh, "interdiff #$old_id #$new_id"); close $interdiff_fh; $ENV{'PATH'} = ''; @@ -676,7 +645,7 @@ sub warn_if_interdiff_might_fail { } sub setup_patch_readers { - my ($diff_root) = @_; + my ($diff_root, $context) = @_; # # Parameters: @@ -700,11 +669,11 @@ sub setup_patch_readers { $last_reader = $last_reader->sends_data_to; } # Add in cvs context if we have the necessary info to do it - if ($::FORM{'context'} ne "patch" && $::cvsbin && Param('cvsroot_get')) + if ($context ne "patch" && $::cvsbin && Param('cvsroot_get')) { require PatchReader::AddCVSContext; $last_reader->sends_data_to( - new PatchReader::AddCVSContext($::FORM{'context'}, + new PatchReader::AddCVSContext($context, Param('cvsroot_get'))); $last_reader = $last_reader->sends_data_to; } @@ -713,20 +682,18 @@ sub setup_patch_readers { sub setup_template_patch_reader { - my ($last_reader) = @_; + my ($last_reader, $format, $context) = @_; require PatchReader::DiffPrinter::template; - my $format = $::FORM{'format'}; - # Define the vars for templates - if (defined($::FORM{'headers'})) { - $vars->{headers} = $::FORM{'headers'}; + if (defined $cgi->param('headers')) { + $vars->{headers} = $cgi->param('headers'); } else { - $vars->{headers} = 1 if !defined($::FORM{'headers'}); + $vars->{headers} = 1 if !defined $cgi->param('headers'); } - $vars->{collapsed} = $::FORM{'collapsed'}; - $vars->{context} = $::FORM{'context'}; + $vars->{collapsed} = $cgi->param('collapsed'); + $vars->{context} = $context; $vars->{do_context} = $::cvsbin && Param('cvsroot_get') && !$vars->{'newid'}; # Print everything out @@ -745,8 +712,14 @@ sub setup_template_patch_reader sub diff { + # Retrieve and validate parameters + my ($attach_id) = validateID(); + my $format = validateFormat('html', 'raw'); + my $context = validateContext(); + # Get patch data - SendSQL("SELECT bug_id, description, ispatch, thedata FROM attachments WHERE attach_id = $::FORM{'id'}"); + SendSQL("SELECT bug_id, description, ispatch, thedata FROM attachments " . + "WHERE attach_id = $attach_id"); my ($bugid, $description, $ispatch, $thedata) = FetchSQLData(); # If it is not a patch, view normally @@ -756,9 +729,9 @@ sub diff return; } - my ($reader, $last_reader) = setup_patch_readers(); + my ($reader, $last_reader) = setup_patch_readers(undef,$context); - if ($::FORM{'format'} eq "raw") + if ($format eq 'raw') { require PatchReader::DiffPrinter::raw; $last_reader->sends_data_to(new PatchReader::DiffPrinter::raw()); @@ -766,7 +739,7 @@ sub diff use vars qw($cgi); print $cgi->header(-type => 'text/plain', -expires => '+3M'); - $reader->iterate_string("Attachment " . $::FORM{'id'}, $thedata); + $reader->iterate_string("Attachment $attach_id", $thedata); } else { @@ -778,7 +751,7 @@ sub diff SendSQL("SELECT attach_id, description FROM attachments WHERE bug_id = $bugid AND ispatch = 1 ORDER BY creation_ts DESC"); my $select_next_patch = 0; while (my ($other_id, $other_desc) = FetchSQLData()) { - if ($other_id eq $::FORM{'id'}) { + if ($other_id eq $attach_id) { $select_next_patch = 1; } else { push @{$vars->{other_patches}}, { id => $other_id, desc => $other_desc, selected => $select_next_patch }; @@ -790,20 +763,24 @@ sub diff } $vars->{bugid} = $bugid; - $vars->{attachid} = $::FORM{'id'}; + $vars->{attachid} = $attach_id; $vars->{description} = $description; - setup_template_patch_reader($last_reader); + setup_template_patch_reader($last_reader, $format, $context); # Actually print out the patch - $reader->iterate_string("Attachment " . $::FORM{'id'}, $thedata); + $reader->iterate_string("Attachment $attach_id", $thedata); } } +# Display all attachments for a given bug in a series of IFRAMEs within one +# HTML page. sub viewall { - # Display all attachments for a given bug in a series of IFRAMEs within one HTML page. + # Retrieve and validate parameters + my $bugid = $cgi->param('bugid'); + ValidateBugID($bugid); - # Retrieve the attachments from the database and write them into an array - # of hashes where each hash represents one attachment. + # Retrieve the attachments from the database and write them into an array + # of hashes where each hash represents one attachment. my $privacy = ""; my $dbh = Bugzilla->dbh; @@ -814,7 +791,7 @@ sub viewall $dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . ", mimetype, description, ispatch, isobsolete, isprivate, LENGTH(thedata) - FROM attachments WHERE bug_id = $::FORM{'bugid'} $privacy + FROM attachments WHERE bug_id = $bugid $privacy ORDER BY attach_id"); my @attachments; # the attachments array while (MoreSQLData()) @@ -833,11 +810,11 @@ sub viewall # Retrieve the bug summary (for displaying on screen) and assignee. SendSQL("SELECT short_desc, assigned_to FROM bugs " . - "WHERE bug_id = $::FORM{'bugid'}"); + "WHERE bug_id = $bugid"); my ($bugsummary, $assignee_id) = FetchSQLData(); # Define the variables and functions that will be passed to the UI template. - $vars->{'bugid'} = $::FORM{'bugid'}; + $vars->{'bugid'} = $bugid; $vars->{'attachments'} = \@attachments; $vars->{'bugassignee_id'} = $assignee_id; $vars->{'bugsummary'} = $bugsummary; @@ -850,20 +827,23 @@ sub viewall || ThrowTemplateError($template->error()); } - +# Display a form for entering a new attachment. sub enter { - # Display a form for entering a new attachment. + # Retrieve and validate parameters + my $bugid = $cgi->param('bugid'); + ValidateBugID($bugid); + validateCanChangeBug($bugid); # Retrieve the attachments the user can edit from the database and write # them into an array of hashes where each hash represents one attachment. my $canEdit = ""; if (!UserInGroup("editbugs")) { - $canEdit = "AND submitter_id = $::userid"; + $canEdit = "AND submitter_id = " . Bugzilla->user->id; } SendSQL("SELECT attach_id, description, isprivate FROM attachments - WHERE bug_id = $::FORM{'bugid'} + WHERE bug_id = $bugid AND isobsolete = 0 $canEdit ORDER BY attach_id"); my @attachments; # the attachments array @@ -877,18 +857,18 @@ sub enter # Retrieve the bug summary (for displaying on screen) and assignee. SendSQL("SELECT short_desc, assigned_to FROM bugs - WHERE bug_id = $::FORM{'bugid'}"); + WHERE bug_id = $bugid"); my ($bugsummary, $assignee_id) = FetchSQLData(); # Define the variables and functions that will be passed to the UI template. - $vars->{'bugid'} = $::FORM{'bugid'}; + $vars->{'bugid'} = $bugid; $vars->{'attachments'} = \@attachments; $vars->{'bugassignee_id'} = $assignee_id; $vars->{'bugsummary'} = $bugsummary; $vars->{'GetBugLink'} = \&GetBugLink; SendSQL("SELECT product_id, component_id FROM bugs - WHERE bug_id = $::FORM{'bugid'}"); + WHERE bug_id = $bugid"); my ($product_id, $component_id) = FetchSQLData(); my $flag_types = Bugzilla::FlagType::match({'target_type' => 'attachment', 'product_id' => $product_id, @@ -904,19 +884,40 @@ sub enter || ThrowTemplateError($template->error()); } - +# Insert a new attachment into the database. sub insert { - my ($data) = @_; + my $dbh = Bugzilla->dbh; + my $userid = Bugzilla->user->id; - # Insert a new attachment into the database. - my $dbh = Bugzilla->dbh; - - # Escape characters in strings that will be used in SQL statements. - $filename = SqlQuote($filename); - my $description = SqlQuote($::FORM{'description'}); - my $contenttype = SqlQuote($::FORM{'contenttype'}); - my $isprivate = $::FORM{'isprivate'} ? 1 : 0; + # Retrieve and validate parameters + my $bugid = $cgi->param('bugid'); + ValidateBugID($bugid); + validateCanChangeBug($bugid); + ValidateComment(scalar $cgi->param('comment')); + my $filename = validateFilename(); + validateIsPatch(); + my $data = validateData(); + validateDescription(); + validateContentType() unless $cgi->param('ispatch'); + + my @obsolete_ids = (); + @obsolete_ids = validateObsolete() if $cgi->param('obsolete'); + + # The order of these function calls is important, as both Flag::validate + # and FlagType::validate assume User::match_field has ensured that the + # values in the requestee fields are legitimate user email addresses. + Bugzilla::User::match_field($cgi, { + '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } + }); + Bugzilla::Flag::validate($cgi, $bugid); + Bugzilla::FlagType::validate($cgi, $bugid, $cgi->param('id')); + + # Escape characters in strings that will be used in SQL statements. + my $sql_filename = SqlQuote($filename); + my $description = SqlQuote($cgi->param('description')); + my $contenttype = SqlQuote($cgi->param('contenttype')); + my $isprivate = $cgi->param('isprivate') ? 1 : 0; # Figure out when the changes were made. my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()"); @@ -926,9 +927,9 @@ sub insert my $sth = $dbh->prepare("INSERT INTO attachments (thedata, bug_id, creation_ts, filename, description, mimetype, ispatch, isprivate, submitter_id) - VALUES (?, $::FORM{'bugid'}, $sql_timestamp, $filename, - $description, $contenttype, $::FORM{'ispatch'}, - $isprivate, $::userid)"); + VALUES (?, $bugid, $sql_timestamp, $sql_filename, + $description, $contenttype, " . $cgi->param('ispatch') . ", + $isprivate, $userid)"); # We only use $data here in this INSERT with a placeholder, # so it's safe. trick_taint($data); @@ -940,7 +941,7 @@ sub insert # If the file is to be stored locally, stream the file from the webserver # to the local file without reading it into a local variable. - if ($::FORM{'bigfile'}) + if ($cgi->param('bigfile')) { my $fh = $cgi->upload('data'); my $hash = ($attachid % 100) + 100; @@ -967,10 +968,11 @@ sub insert # Insert a comment about the new attachment into the database. - my $comment = "Created an attachment (id=$attachid)\n$::FORM{'description'}\n"; - $comment .= ("\n" . $::FORM{'comment'}) if $::FORM{'comment'}; + my $comment = "Created an attachment (id=$attachid)\n" . + $cgi->param('description') . "\n"; + $comment .= ("\n" . $cgi->param('comment')) if defined $cgi->param('comment'); - AppendComment($::FORM{'bugid'}, + AppendComment($bugid, Bugzilla->user->login, $comment, $isprivate, @@ -978,20 +980,23 @@ sub insert # Make existing attachments obsolete. my $fieldid = GetFieldID('attachments.isobsolete'); - foreach my $obsolete_id (@{$::MFORM{'obsolete'}}) { + foreach my $obsolete_id (@obsolete_ids) { # If the obsolete attachment has request flags, cancel them. # This call must be done before updating the 'attachments' table. - Bugzilla::Flag::CancelRequests($::FORM{'bugid'}, $obsolete_id, $sql_timestamp); - - SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $obsolete_id"); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($::FORM{'bugid'}, $obsolete_id, $::userid, $sql_timestamp, $fieldid, '0', '1')"); + Bugzilla::Flag::CancelRequests($bugid, $obsolete_id, $sql_timestamp); + + SendSQL("UPDATE attachments SET isobsolete = 1 " . + "WHERE attach_id = $obsolete_id"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $obsolete_id, $userid, $sql_timestamp, $fieldid, + '0', '1')"); } # Assign the bug to the user, if they are allowed to take it my $owner = ""; - if ($::FORM{'takebug'} && UserInGroup("editbugs")) { + if ($cgi->param('takebug') && UserInGroup("editbugs")) { my @fields = ("assigned_to", "bug_status", "resolution", "login_name"); @@ -1000,10 +1005,10 @@ sub insert "FROM bugs " . "INNER JOIN profiles " . "ON profiles.userid = bugs.assigned_to " . - "WHERE bugs.bug_id = $::FORM{'bugid'}"); + "WHERE bugs.bug_id = $bugid"); my @oldvalues = FetchSQLData(); - my @newvalues = ($::userid, "ASSIGNED", "", DBID_to_name($::userid)); + my @newvalues = ($userid, "ASSIGNED", "", Bugzilla->user->login); # Make sure the person we are taking the bug from gets mail. $owner = $oldvalues[3]; @@ -1014,7 +1019,7 @@ sub insert # Update the bug record. Note that this doesn't involve login_name. SendSQL("UPDATE bugs SET delta_ts = $sql_timestamp, " . join(", ", map("$fields[$_] = $newvalues[$_]", (0..2))) . - " WHERE bug_id = $::FORM{'bugid'}"); + " WHERE bug_id = $bugid"); # We store email addresses in the bugs_activity table rather than IDs. $oldvalues[0] = $oldvalues[3]; @@ -1026,9 +1031,8 @@ sub insert my $fieldid = GetFieldID($fields[$i]); SendSQL("INSERT INTO bugs_activity " . "(bug_id, who, bug_when, fieldid, removed, added) " . - " VALUES ($::FORM{'bugid'}, $::userid, " . - "$sql_timestamp " . - ", $fieldid, $oldvalues[$i], $newvalues[$i])"); + "VALUES ($bugid, $userid, $sql_timestamp, " . + "$fieldid, $oldvalues[$i], $newvalues[$i])"); } } } @@ -1040,17 +1044,11 @@ sub insert # Define the variables and functions that will be passed to the UI template. $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login, 'owner' => $owner }; - my $bugid = $::FORM{'bugid'}; - detaint_natural($bugid); # don't bother with error condition, we know it'll work - # because of ValidateBugID above. This is only needed - # for Perl 5.6.0. If we ever require Perl 5.6.1 or - # newer, or detaint something other than $::FORM{'bugid'} - # in ValidateBugID above, then this can go away. $vars->{'bugid'} = $bugid; $vars->{'attachid'} = $attachid; $vars->{'description'} = $description; - $vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'}; - $vars->{'contenttype'} = $::FORM{'contenttype'}; + $vars->{'contenttypemethod'} = $cgi->param('contenttypemethod'); + $vars->{'contenttype'} = $cgi->param('contenttype'); print Bugzilla->cgi->header(); @@ -1059,18 +1057,20 @@ sub insert || ThrowTemplateError($template->error()); } - +# Edit an attachment record. Users with "editbugs" privileges, (or the +# original attachment's submitter) can edit the attachment's description, +# content type, ispatch and isobsolete flags, and statuses, and they can +# also submit a comment that appears in the bug. +# Users cannot edit the content of the attachment itself. sub edit { - # Edit an attachment record. Users with "editbugs" privileges, (or the - # original attachment's submitter) can edit the attachment's description, - # content type, ispatch and isobsolete flags, and statuses, and they can - # also submit a comment that appears in the bug. - # Users cannot edit the content of the attachment itself. + # Retrieve and validate parameters + my ($attach_id) = validateID(); + validateCanEdit($attach_id); # Retrieve the attachment from the database. SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isobsolete, isprivate, LENGTH(thedata) - FROM attachments WHERE attach_id = $::FORM{'id'}"); + FROM attachments WHERE attach_id = $attach_id"); my ($description, $contenttype, $filename, $bugid, $ispatch, $isobsolete, $isprivate, $datasize) = FetchSQLData(); my $isviewable = isViewable($contenttype); @@ -1091,14 +1091,14 @@ sub edit 'component_id' => $component_id }); foreach my $flag_type (@$flag_types) { $flag_type->{'flags'} = Bugzilla::Flag::match({ 'type_id' => $flag_type->{'id'}, - 'attach_id' => $::FORM{'id'}, + 'attach_id' => $attach_id, 'is_active' => 1 }); } $vars->{'flag_types'} = $flag_types; $vars->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types); # Define the variables and functions that will be passed to the UI template. - $vars->{'attachid'} = $::FORM{'id'}; + $vars->{'attachid'} = $attach_id; $vars->{'description'} = $description; $vars->{'contenttype'} = $contenttype; $vars->{'filename'} = $filename; @@ -1124,15 +1124,31 @@ sub edit || ThrowTemplateError($template->error()); } - +# Updates an attachment record. sub update { - # Updates an attachment record. my $dbh = Bugzilla->dbh; - - # Get the bug ID for the bug to which this attachment is attached. - SendSQL("SELECT bug_id FROM attachments WHERE attach_id = $::FORM{'id'}"); - my $bugid = FetchSQLData(); + my $userid = Bugzilla->user->id; + + # Retrieve and validate parameters + ValidateComment(scalar $cgi->param('comment')); + my ($attach_id, $bugid) = validateID(); + validateCanEdit($attach_id); + validateCanChangeAttachment($attach_id); + validateDescription(); + validateIsPatch(); + validateContentType() unless $cgi->param('ispatch'); + validateIsObsolete(); + validatePrivate(); + + # The order of these function calls is important, as both Flag::validate + # and FlagType::validate assume User::match_field has ensured that the + # values in the requestee fields are legitimate user email addresses. + Bugzilla::User::match_field($cgi, { + '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } + }); + Bugzilla::Flag::validate($cgi, $bugid); + Bugzilla::FlagType::validate($cgi, $bugid, $attach_id); # Lock database tables in preparation for updating the attachment. $dbh->bz_lock_tables('attachments WRITE', 'flags WRITE' , @@ -1152,14 +1168,14 @@ sub update # Get a copy of the attachment record before we make changes # so we can record those changes in the activity table. SendSQL("SELECT description, mimetype, filename, ispatch, isobsolete, isprivate - FROM attachments WHERE attach_id = $::FORM{'id'}"); + FROM attachments WHERE attach_id = $attach_id"); my ($olddescription, $oldcontenttype, $oldfilename, $oldispatch, $oldisobsolete, $oldisprivate) = FetchSQLData(); # Quote the description and content type for use in the SQL UPDATE statement. - my $quoteddescription = SqlQuote($::FORM{'description'}); - my $quotedcontenttype = SqlQuote($::FORM{'contenttype'}); - my $quotedfilename = SqlQuote($::FORM{'filename'}); + my $quoteddescription = SqlQuote($cgi->param('description')); + my $quotedcontenttype = SqlQuote($cgi->param('contenttype')); + my $quotedfilename = SqlQuote($cgi->param('filename')); # Figure out when the changes were made. SendSQL("SELECT NOW()"); @@ -1169,7 +1185,7 @@ sub update # to attachments so that we can delete pending requests if the user # is obsoleting this attachment without deleting any requests # the user submits at the same time. - my $target = Bugzilla::Flag::GetTarget(undef, $::FORM{'id'}); + my $target = Bugzilla::Flag::GetTarget(undef, $attach_id); Bugzilla::Flag::process($target, $timestamp, $cgi); # Update the attachment record in the database. @@ -1177,71 +1193,83 @@ sub update SET description = $quoteddescription , mimetype = $quotedcontenttype , filename = $quotedfilename , - ispatch = $::FORM{'ispatch'}, - isobsolete = $::FORM{'isobsolete'} , - isprivate = $::FORM{'isprivate'} - WHERE attach_id = $::FORM{'id'} + ispatch = " . $cgi->param('ispatch') . ", + isobsolete = " . $cgi->param('isobsolete') . ", + isprivate = " . $cgi->param('isprivate') . " + WHERE attach_id = $attach_id "); # Record changes in the activity table. my $sql_timestamp = SqlQuote($timestamp); - if ($olddescription ne $::FORM{'description'}) { + if ($olddescription ne $cgi->param('description')) { my $quotedolddescription = SqlQuote($olddescription); my $fieldid = GetFieldID('attachments.description'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $quotedolddescription, $quoteddescription)"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $attach_id, $userid, $sql_timestamp, $fieldid, + $quotedolddescription, $quoteddescription)"); } - if ($oldcontenttype ne $::FORM{'contenttype'}) { + if ($oldcontenttype ne $cgi->param('contenttype')) { my $quotedoldcontenttype = SqlQuote($oldcontenttype); my $fieldid = GetFieldID('attachments.mimetype'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $quotedoldcontenttype, $quotedcontenttype)"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $attach_id, $userid, $sql_timestamp, $fieldid, + $quotedoldcontenttype, $quotedcontenttype)"); } - if ($oldfilename ne $::FORM{'filename'}) { + if ($oldfilename ne $cgi->param('filename')) { my $quotedoldfilename = SqlQuote($oldfilename); my $fieldid = GetFieldID('attachments.filename'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $quotedoldfilename, $quotedfilename)"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $attach_id, $userid, $sql_timestamp, $fieldid, + $quotedoldfilename, $quotedfilename)"); } - if ($oldispatch ne $::FORM{'ispatch'}) { + if ($oldispatch ne $cgi->param('ispatch')) { my $fieldid = GetFieldID('attachments.ispatch'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $oldispatch, $::FORM{'ispatch'})"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $attach_id, $userid, $sql_timestamp, $fieldid, + $oldispatch, " . $cgi->param('ispatch') . ")"); } - if ($oldisobsolete ne $::FORM{'isobsolete'}) { + if ($oldisobsolete ne $cgi->param('isobsolete')) { my $fieldid = GetFieldID('attachments.isobsolete'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $oldisobsolete, $::FORM{'isobsolete'})"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $attach_id, $userid, $sql_timestamp, $fieldid, + $oldisobsolete, " . $cgi->param('isobsolete') . ")"); } - if ($oldisprivate ne $::FORM{'isprivate'}) { + if ($oldisprivate ne $cgi->param('isprivate')) { my $fieldid = GetFieldID('attachments.isprivate'); - SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added) - VALUES ($bugid, $::FORM{'id'}, $::userid, $sql_timestamp, $fieldid, $oldisprivate, $::FORM{'isprivate'})"); + SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, + fieldid, removed, added) + VALUES ($bugid, $attach_id, $userid, $sql_timestamp, $fieldid, + $oldisprivate, " . $cgi->param('isprivate') . ")"); } # Unlock all database tables now that we are finished updating the database. $dbh->bz_unlock_tables(); + # Get the user's login name since the AppendComment and header functions + # need it. + my $who = Bugzilla->user->login; + # If the user submitted a comment while editing the attachment, # add the comment to the bug. - if ( $::FORM{'comment'} ) + if (defined $cgi->param('comment')) { - # Prepend a string to the comment to let users know that the comment came from - # the "edit attachment" screen. - my $comment = qq|(From update of attachment $::FORM{'id'})\n| . $::FORM{'comment'}; - - # Get the user's login name since the AppendComment function needs it. - my $who = DBID_to_name($::userid); - # Mention $::userid again so Perl doesn't give me a warning about it. - my $neverused = $::userid; + # Prepend a string to the comment to let users know that the comment came + # from the "edit attachment" screen. + my $comment = qq|(From update of attachment $attach_id)\n| . + $cgi->param('comment'); # Append the comment to the list of comments in the database. - AppendComment($bugid, $who, $comment, $::FORM{'isprivate'}, $timestamp); + AppendComment($bugid, $who, $comment, $cgi->param('isprivate'), $timestamp); } # Define the variables and functions that will be passed to the UI template. - $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login }; - $vars->{'attachid'} = $::FORM{'id'}; + $vars->{'mailrecipients'} = { 'changer' => $who }; + $vars->{'attachid'} = $attach_id; $vars->{'bugid'} = $bugid; print Bugzilla->cgi->header(); -- cgit v1.2.3-65-gdbad From 95859bf15300cddd1ece82e8224367638f956f20 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Thu, 28 Apr 2005 09:14:25 +0000 Subject: Bug 274724: The 'Edit Attachment' link is now available even if a user does not have 'editbugs' privs - Patch by Frédéric Buclin r=myk a=myk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Attachment.pm | 41 ++++++----------- attachment.cgi | 23 ++++------ template/en/default/attachment/list.html.tmpl | 65 +++++++++++++-------------- 3 files changed, 53 insertions(+), 76 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 1a1246d86..4d223d633 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -64,34 +64,28 @@ sub new { sub query { # Retrieves and returns an array of attachment records for a given bug. - # This data should be given to attachment/list.atml in an + # This data should be given to attachment/list.html.tmpl in an # "attachments" variable. my ($bugid) = @_; my $dbh = Bugzilla->dbh; - my $in_editbugs = UserInGroup("editbugs"); - &::SendSQL("SELECT product_id - FROM bugs - WHERE bug_id = $bugid"); - my $productid = &::FetchOneColumn(); - my $caneditproduct = &::CanEditProductId($productid); - # Retrieve a list of attachments for this bug and write them into an array # of hashes in which each hash represents a single attachment. - &::SendSQL("SELECT attach_id, " . - $dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . - ", mimetype, description, ispatch, isobsolete, isprivate, - submitter_id, LENGTH(thedata) - FROM attachments WHERE bug_id = $bugid ORDER BY attach_id - "); + my $list = $dbh->selectall_arrayref("SELECT attach_id, " . + $dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . + ", mimetype, description, ispatch, + isobsolete, isprivate, LENGTH(thedata) + FROM attachments + WHERE bug_id = ? ORDER BY attach_id", + undef, $bugid); + my @attachments = (); - while (&::MoreSQLData()) { + foreach my $row (@$list) { my %a; - my $submitter_id; - ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'}, - $a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id, - $a{'datasize'}) = &::FetchSQLData(); + ($a{'attachid'}, $a{'date'}, $a{'contenttype'}, + $a{'description'}, $a{'ispatch'}, $a{'isobsolete'}, + $a{'isprivate'}, $a{'datasize'}) = @$row; # Retrieve a list of flags for this attachment. $a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'}, @@ -107,16 +101,9 @@ sub query close(AH); } } - - # We will display the edit link if the user can edit the attachment; - # ie the are the submitter, or they have canedit. - # Also show the link if the user is not logged in - in that cae, - # They'll be prompted later - $a{'canedit'} = ($::userid == 0 || (($submitter_id == $::userid || - $in_editbugs) && $caneditproduct)); push @attachments, \%a; } - + return \@attachments; } diff --git a/attachment.cgi b/attachment.cgi index 2b119e7ff..8b9bdaafd 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -197,13 +197,6 @@ sub validateCanEdit { my ($attach_id) = (@_); - # If the user is not logged in, claim that they can edit. This allows - # the edit screen to be displayed to people who aren't logged in. - # People not logged in can't actually commit changes, because that code - # calls Bugzilla->login with LOGIN_REQUIRED, not with LOGIN_NORMAL, - # before calling this sub - return unless Bugzilla->user; - # People in editbugs can edit all attachments return if UserInGroup("editbugs"); @@ -1057,16 +1050,14 @@ sub insert || ThrowTemplateError($template->error()); } -# Edit an attachment record. Users with "editbugs" privileges, (or the -# original attachment's submitter) can edit the attachment's description, -# content type, ispatch and isobsolete flags, and statuses, and they can -# also submit a comment that appears in the bug. -# Users cannot edit the content of the attachment itself. +# Displays a form for editing attachment properties. +# Any user is allowed to access this page, unless the attachment +# is private and the user does not belong to the insider group. +# Validations are done later when the user submits changes. sub edit { # Retrieve and validate parameters my ($attach_id) = validateID(); - validateCanEdit($attach_id); # Retrieve the attachment from the database. SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isobsolete, isprivate, LENGTH(thedata) @@ -1124,7 +1115,11 @@ sub edit || ThrowTemplateError($template->error()); } -# Updates an attachment record. +# Updates an attachment record. Users with "editbugs" privileges, (or the +# original attachment's submitter) can edit the attachment's description, +# content type, ispatch and isobsolete flags, and statuses, and they can +# also submit a comment that appears in the bug. +# Users cannot edit the content of the attachment itself. sub update { my $dbh = Bugzilla->dbh; diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index 41115ab9c..5840139a3 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -33,29 +33,29 @@ [% canseeprivate = !Param("insidergroup") || UserInGroup(Param("insidergroup")) %] [% FOREACH attachment = attachments %] - [% IF !attachment.isprivate || canseeprivate %] - - - [% attachment.description FILTER html FILTER obsolete(attachment.isobsolete) %] - - - - [% IF attachment.ispatch %] - patch - [% ELSE %] - [% attachment.contenttype FILTER html %] - [% END %] - - - [% attachment.date FILTER time %] - [% attachment.datasize FILTER unitconvert %] + [% IF !attachment.isprivate || canseeprivate %] + + + [% attachment.description FILTER html FILTER obsolete(attachment.isobsolete) %] + - [% IF show_attachment_flags %] - [% IF attachment.flags.size == 0 %] - none + [% IF attachment.ispatch %] + patch [% ELSE %] - [% FOREACH flag = attachment.flags %] + [% attachment.contenttype FILTER html %] + [% END %] + + + [% attachment.date FILTER time %] + [% attachment.datasize FILTER unitconvert %] + + [% IF show_attachment_flags %] + + [% IF attachment.flags.size == 0 %] + none + [% ELSE %] + [% FOREACH flag = attachment.flags %] [% IF flag.setter %] [% flag.setter.nick FILTER html %]: [% END %] @@ -63,24 +63,19 @@ [%+ IF flag.status == "?" && flag.requestee %] ([% flag.requestee.nick FILTER html %]) [% END %]
+ [% END %] [% END %] - [% END %] - - [% END %] - - - [% IF attachment.canedit %] - Edit + [% END %] - [% IF attachment.ispatch && patchviewerinstalled %] - [% IF attachment.canedit %] - | + + + Edit + [% IF attachment.ispatch && patchviewerinstalled %] + | Diff [% END %] - Diff - [% END %] - - - [% END %] + + + [% END %] [% END %] -- cgit v1.2.3-65-gdbad From 4436350287ea47c1714a2ae583895f04f84a6231 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Tue, 21 Jun 2005 03:04:24 +0000 Subject: Bug 180792: Setting and/or updating flags does not update last changed date - Patch by Frédéric Buclin r/a=myk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Flag.pm | 3 +++ attachment.cgi | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 10cbcd0d0..765ad9afa 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -447,6 +447,9 @@ sub update_activity { (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bug_id, $attach_id, $::userid, $timestamp, $field_id, $sql_removed, $sql_added)"); + + $dbh->do("UPDATE bugs SET delta_ts = $timestamp WHERE bug_id = ?", + undef, $bug_id); } } diff --git a/attachment.cgi b/attachment.cgi index 8b9bdaafd..0ed608c52 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1251,7 +1251,7 @@ sub update # If the user submitted a comment while editing the attachment, # add the comment to the bug. - if (defined $cgi->param('comment')) + if ($cgi->param('comment')) { # Prepend a string to the comment to let users know that the comment came # from the "edit attachment" screen. -- cgit v1.2.3-65-gdbad From 0b8d70a6a120da140ed41b37c5d98a902e16cae4 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Tue, 21 Jun 2005 05:20:49 +0000 Subject: Backing out bug 180792. A table is locked as READ while my patch tries to write in it :( --- Bugzilla/Flag.pm | 3 --- attachment.cgi | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 765ad9afa..10cbcd0d0 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -447,9 +447,6 @@ sub update_activity { (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bug_id, $attach_id, $::userid, $timestamp, $field_id, $sql_removed, $sql_added)"); - - $dbh->do("UPDATE bugs SET delta_ts = $timestamp WHERE bug_id = ?", - undef, $bug_id); } } diff --git a/attachment.cgi b/attachment.cgi index 0ed608c52..8b9bdaafd 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1251,7 +1251,7 @@ sub update # If the user submitted a comment while editing the attachment, # add the comment to the bug. - if ($cgi->param('comment')) + if (defined $cgi->param('comment')) { # Prepend a string to the comment to let users know that the comment came # from the "edit attachment" screen. -- cgit v1.2.3-65-gdbad From 7f9a55a1573e31ca4189fb9a379960ede2200f4b Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Tue, 21 Jun 2005 05:46:39 +0000 Subject: Bug 180792: Setting and/or updating flags does not update last changed date - Patch by Frédéric Buclin r/a=myk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Flag.pm | 3 +++ attachment.cgi | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 10cbcd0d0..765ad9afa 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -447,6 +447,9 @@ sub update_activity { (bug_id, attach_id, who, bug_when, fieldid, removed, added) VALUES ($bug_id, $attach_id, $::userid, $timestamp, $field_id, $sql_removed, $sql_added)"); + + $dbh->do("UPDATE bugs SET delta_ts = $timestamp WHERE bug_id = ?", + undef, $bug_id); } } diff --git a/attachment.cgi b/attachment.cgi index 8b9bdaafd..33f35b69f 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -1156,7 +1156,7 @@ sub update # Bugzilla::User needs to rederive groups. profiles and # user_group_map would be READ locks instead of WRITE locks if it # weren't for derive_groups, which needs to write to those tables. - 'bugs READ', 'profiles WRITE', 'email_setting READ', + 'bugs WRITE', 'profiles WRITE', 'email_setting READ', 'cc READ', 'bug_group_map READ', 'user_group_map WRITE', 'group_group_map READ', 'groups READ'); @@ -1251,7 +1251,7 @@ sub update # If the user submitted a comment while editing the attachment, # add the comment to the bug. - if (defined $cgi->param('comment')) + if ($cgi->param('comment')) { # Prepend a string to the comment to let users know that the comment came # from the "edit attachment" screen. -- cgit v1.2.3-65-gdbad From dce0cf73010c7ed17f98a5b19148390d11788ef7 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Fri, 8 Jul 2005 01:42:08 +0000 Subject: Bug 240251: Bug::AppendComment() should receive the user ID as a 2nd parameter - Patch by Frédéric Buclin r=wurblzap a=justdave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Bug.pm | 5 ++--- attachment.cgi | 14 +++----------- process_bug.cgi | 4 ++-- 3 files changed, 7 insertions(+), 16 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 40ed06631..fae0decc0 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -696,7 +696,7 @@ sub bug_alias_to_id ($) { ##################################################################### sub AppendComment ($$$;$$$) { - my ($bugid, $who, $comment, $isprivate, $timestamp, $work_time) = @_; + my ($bugid, $whoid, $comment, $isprivate, $timestamp, $work_time) = @_; $work_time ||= 0; my $dbh = Bugzilla->dbh; @@ -717,7 +717,6 @@ sub AppendComment ($$$;$$$) { # Comments are always safe, because we always display their raw contents, # and we use them in a placeholder below. trick_taint($comment); - my $whoid = &::DBNameToIdAndCheck($who); my $privacyval = $isprivate ? 1 : 0 ; $dbh->do(q{INSERT INTO longdescs (bug_id, who, bug_when, thetext, isprivate, work_time) @@ -968,7 +967,7 @@ sub CheckIfVotedConfirmed { "VALUES (?, ?, ?, ?, ?, ?)", undef, ($id, $who, $timestamp, $fieldid, '0', '1')); - AppendComment($id, &::DBID_to_name($who), + AppendComment($id, $who, "*** This bug has been confirmed by popular vote. ***", 0, $timestamp); diff --git a/attachment.cgi b/attachment.cgi index 33f35b69f..1971f93a3 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -965,11 +965,7 @@ sub insert $cgi->param('description') . "\n"; $comment .= ("\n" . $cgi->param('comment')) if defined $cgi->param('comment'); - AppendComment($bugid, - Bugzilla->user->login, - $comment, - $isprivate, - $timestamp); + AppendComment($bugid, $userid, $comment, $isprivate, $timestamp); # Make existing attachments obsolete. my $fieldid = GetFieldID('attachments.isobsolete'); @@ -1245,10 +1241,6 @@ sub update # Unlock all database tables now that we are finished updating the database. $dbh->bz_unlock_tables(); - # Get the user's login name since the AppendComment and header functions - # need it. - my $who = Bugzilla->user->login; - # If the user submitted a comment while editing the attachment, # add the comment to the bug. if ($cgi->param('comment')) @@ -1259,11 +1251,11 @@ sub update $cgi->param('comment'); # Append the comment to the list of comments in the database. - AppendComment($bugid, $who, $comment, $cgi->param('isprivate'), $timestamp); + AppendComment($bugid, $userid, $comment, $cgi->param('isprivate'), $timestamp); } # Define the variables and functions that will be passed to the UI template. - $vars->{'mailrecipients'} = { 'changer' => $who }; + $vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login }; $vars->{'attachid'} = $attach_id; $vars->{'bugid'} = $bugid; diff --git a/process_bug.cgi b/process_bug.cgi index f2f71710d..1fa8400e9 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -1346,7 +1346,7 @@ foreach my $id (@idlist) { } if ($cgi->param('comment') || $work_time) { - AppendComment($id, Bugzilla->user->login, $cgi->param('comment'), + AppendComment($id, $whoid, $cgi->param('comment'), $cgi->param('commentprivacy'), $timestamp, $work_time); $bug_changed = 1; } @@ -1775,7 +1775,7 @@ foreach my $id (@idlist) { "VALUES ($reporter, $duplicate)"); } # Bug 171639 - Duplicate notifications do not need to be private. - AppendComment($duplicate, Bugzilla->user->login, + AppendComment($duplicate, $whoid, "*** Bug " . $cgi->param('id') . " has been marked as a duplicate of this bug. ***", 0, $timestamp); -- cgit v1.2.3-65-gdbad From 6bff5c39e564cc34c85c4d30e11f6ff14482548a Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Fri, 8 Jul 2005 09:18:22 +0000 Subject: Bug 87404: Attachments don't work if you need to use user matching Patch By Max Kanat-Alexander r=LpSolit, a=justdave --- Bugzilla/User.pm | 56 ++++++++++++++++++++++++++- attachment.cgi | 16 ++++++-- template/en/default/global/messages.html.tmpl | 11 ++++++ 3 files changed, 79 insertions(+), 4 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 3e68647c6..ff3d38721 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -48,8 +48,20 @@ use base qw(Exporter); @Bugzilla::User::EXPORT = qw(insert_new_user is_available_username login_to_id UserInGroup + USER_MATCH_MULTIPLE USER_MATCH_FAILED USER_MATCH_SUCCESS + MATCH_SKIP_CONFIRM ); +##################################################################### +# Constants +##################################################################### + +use constant USER_MATCH_MULTIPLE => -1; +use constant USER_MATCH_FAILED => 0; +use constant USER_MATCH_SUCCESS => 1; + +use constant MATCH_SKIP_CONFIRM => 1; + ################################################################################ # Functions ################################################################################ @@ -723,6 +735,11 @@ sub match { # searchable fields have been replaced by exact fields and the calling script # is executed as normal. # +# You also have the choice of *never* displaying the confirmation screen. +# In this case, match_field will return one of the three USER_MATCH +# constants described in the POD docs. To make match_field behave this +# way, pass in MATCH_SKIP_CONFIRM as the third argument. +# # match_field must be called early in a script, before anything external is # done with the form data. # @@ -744,9 +761,11 @@ sub match { sub match_field { my $cgi = shift; # CGI object to look up fields in my $fields = shift; # arguments as a hash + my $behavior = shift || 0; # A constant that tells us how to act my $matches = {}; # the values sent to the template my $matchsuccess = 1; # did the match fail? my $need_confirm = 0; # whether to display confirmation screen + my $match_multiple = 0; # whether we ever matched more than one user # prepare default form values @@ -881,6 +900,7 @@ sub match_field { elsif ((scalar(@{$users}) > 1) && (&::Param('maxusermatches') != 1)) { $need_confirm = 1; + $match_multiple = 1; if ((&::Param('maxusermatches')) && (scalar(@{$users}) > &::Param('maxusermatches'))) @@ -899,7 +919,19 @@ sub match_field { } } - return 1 unless $need_confirm; # skip confirmation if not needed. + my $retval; + if (!$matchsuccess) { + $retval = USER_MATCH_FAILED; + } + elsif ($match_multiple) { + $retval = USER_MATCH_MULTIPLE; + } + else { + $retval = USER_MATCH_SUCCESS; + } + + # Skip confirmation if we were told to, or if we don't need to confirm. + return $retval if ($behavior == MATCH_SKIP_CONFIRM || !$need_confirm); $vars->{'script'} = $ENV{'SCRIPT_NAME'}; # for self-referencing URLs $vars->{'fields'} = $fields; # fields being matched @@ -1219,6 +1251,28 @@ there is currently no way to modify a user from this package. Note that the currently logged in user (if any) is available via Luser|Bugzilla/"user">. +=head1 CONSTANTS + +=item C + +Returned by C when at least one field matched more than +one user, but no matches failed. + +=item C + +Returned by C when at least one field failed to match +anything. + +=item C + +Returned by C when all fields successfully matched only one +user. + +=item C + +Passed in to match_field to tell match_field to never display a +confirmation screen. + =head1 METHODS =over 4 diff --git a/attachment.cgi b/attachment.cgi index 1971f93a3..0c010a061 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -23,6 +23,7 @@ # Daniel Raichle # Dave Miller # Alexander J. Vincent +# Max Kanat-Alexander ################################################################################ # Script Initialization @@ -900,9 +901,18 @@ sub insert # The order of these function calls is important, as both Flag::validate # and FlagType::validate assume User::match_field has ensured that the # values in the requestee fields are legitimate user email addresses. - Bugzilla::User::match_field($cgi, { - '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } - }); + my $match_status = Bugzilla::User::match_field($cgi, { + '^requestee(_type)?-(\d+)$' => { 'type' => 'single' }, + }, MATCH_SKIP_CONFIRM); + + $vars->{'match_field'} = 'requestee'; + if ($match_status == USER_MATCH_FAILED) { + $vars->{'message'} = 'user_match_failed'; + } + elsif ($match_status == USER_MATCH_MULTIPLE) { + $vars->{'message'} = 'user_match_multiple'; + } + Bugzilla::Flag::validate($cgi, $bugid); Bugzilla::FlagType::validate($cgi, $bugid, $cgi->param('id')); diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index f9087a21f..30855ed37 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -17,6 +17,7 @@ # Rights Reserved. # # Contributor(s): Gervase Markham + # Max Kanat-Alexander #%] [%# This is a list of all the possible messages. Please keep them in @@ -236,6 +237,16 @@ [% ELSIF message_tag == "shutdown" %] [% title = "$terms.Bugzilla is Down" %] [% Param("shutdownhtml") %] + + [% ELSIF message_tag == "user_match_failed" %] + You entered a username that did not match any known + [% terms.Bugzilla %] users, so we have instead left + the [% match_field FILTER html %] field blank. + + [% ELSIF message_tag == "user_match_multiple" %] + You entered a username that matched more than one + user, so we have instead left the [% match_field FILTER html %] + field blank. [% ELSE %] [%# Give sensible error if error functions are used incorrectly. -- cgit v1.2.3-65-gdbad From 0d7a4fbf959a1c522350786e83df580476bf5642 Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Fri, 8 Jul 2005 12:29:14 +0000 Subject: Bug 293159: [SECURITY] Anyone can change flags and access bug summaries due to a bad check in Flag::validate() and Flag::modify() Patch By Frederic Buclin r=myk, a=justdave --- Bugzilla/Flag.pm | 124 ++++++++++++++++-------- Bugzilla/FlagType.pm | 48 +++++++-- attachment.cgi | 9 +- process_bug.cgi | 11 +-- template/en/default/global/code-error.html.tmpl | 27 ++++++ 5 files changed, 159 insertions(+), 60 deletions(-) (limited to 'attachment.cgi') diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index f19369c24..b0a0586c2 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -235,17 +235,47 @@ Validates fields containing flag modifications. =cut sub validate { + my ($cgi, $bug_id, $attach_id) = @_; + my $user = Bugzilla->user; - my ($cgi, $bug_id) = @_; - + my $dbh = Bugzilla->dbh; + # Get a list of flags to validate. Uses the "map" function # to extract flag IDs from form field names by matching fields # whose name looks like "flag-nnn", where "nnn" is the ID, # and returning just the ID portion of matching field names. my @ids = map(/^flag-(\d+)$/ ? $1 : (), $cgi->param()); - - foreach my $id (@ids) - { + + return unless scalar(@ids); + + # No flag reference should exist when changing several bugs at once. + ThrowCodeError("flags_not_available", { type => 'b' }) unless $bug_id; + + # No reference to existing flags should exist when creating a new + # attachment. + if ($attach_id && ($attach_id < 0)) { + ThrowCodeError("flags_not_available", { type => 'a' }); + } + + # Make sure all flags belong to the bug/attachment they pretend to be. + my $field = ($attach_id) ? "attach_id" : "bug_id"; + my $field_id = $attach_id || $bug_id; + my $not = ($attach_id) ? "" : "NOT"; + + my $invalid_data = + $dbh->selectrow_array("SELECT 1 FROM flags + WHERE id IN (" . join(',', @ids) . ") + AND ($field != ? OR attach_id IS $not NULL) " . + $dbh->sql_limit(1), + undef, $field_id); + + if ($invalid_data) { + ThrowCodeError("invalid_flag_association", + { bug_id => $bug_id, + attach_id => $attach_id }); + } + + foreach my $id (@ids) { my $status = $cgi->param("flag-$id"); # Make sure the flag exists. @@ -269,48 +299,60 @@ sub validate { ThrowCodeError("flag_status_invalid", { id => $id, status => $status }); } - + + # Make sure the user didn't specify a requestee unless the flag + # is specifically requestable. If the requestee was set before + # the flag became specifically unrequestable, leave it as is. + my $old_requestee = + $flag->{'requestee'} ? $flag->{'requestee'}->login : ''; + my $new_requestee = trim($cgi->param("requestee-$id") || ''); + + if ($status eq '?' + && !$flag->{type}->{is_requesteeble} + && $new_requestee + && ($new_requestee ne $old_requestee)) + { + ThrowCodeError("flag_requestee_disabled", + { name => $flag->{type}->{name} }); + } + # Make sure the requestee is authorized to access the bug. # (and attachment, if this installation is using the "insider group" # feature and the attachment is marked private). if ($status eq '?' && $flag->{type}->{is_requesteeble} - && trim($cgi->param("requestee-$id"))) + && $new_requestee + && ($old_requestee ne $new_requestee)) { - my $requestee_email = trim($cgi->param("requestee-$id")); - my $old_requestee = - $flag->{'requestee'} ? $flag->{'requestee'}->login : ''; - - if ($old_requestee ne $requestee_email) { - # We know the requestee exists because we ran - # Bugzilla::User::match_field before getting here. - my $requestee = Bugzilla::User->new_from_login($requestee_email); - - # Throw an error if the user can't see the bug. - if (!$requestee->can_see_bug($bug_id)) - { - ThrowUserError("flag_requestee_unauthorized", - { flag_type => $flag->{'type'}, - requestee => $requestee, - bug_id => $bug_id, - attach_id => - $flag->{target}->{attachment}->{id} }); - } - - # Throw an error if the target is a private attachment and - # the requestee isn't in the group of insiders who can see it. - if ($flag->{target}->{attachment}->{exists} - && $cgi->param('isprivate') - && Param("insidergroup") - && !$requestee->in_group(Param("insidergroup"))) - { - ThrowUserError("flag_requestee_unauthorized_attachment", - { flag_type => $flag->{'type'}, - requestee => $requestee, - bug_id => $bug_id, - attach_id => - $flag->{target}->{attachment}->{id} }); - } + # We know the requestee exists because we ran + # Bugzilla::User::match_field before getting here. + my $requestee = Bugzilla::User->new_from_login($new_requestee); + + # Throw an error if the user can't see the bug. + # Note that if permissions on this bug are changed, + # can_see_bug() will refer to old settings. + if (!$requestee->can_see_bug($bug_id)) { + ThrowUserError("flag_requestee_unauthorized", + { flag_type => $flag->{'type'}, + requestee => $requestee, + bug_id => $bug_id, + attach_id => + $flag->{target}->{attachment}->{id} }); + } + + # Throw an error if the target is a private attachment and + # the requestee isn't in the group of insiders who can see it. + if ($flag->{target}->{attachment}->{exists} + && $cgi->param('isprivate') + && Param("insidergroup") + && !$requestee->in_group(Param("insidergroup"))) + { + ThrowUserError("flag_requestee_unauthorized_attachment", + { flag_type => $flag->{'type'}, + requestee => $requestee, + bug_id => $bug_id, + attach_id => + $flag->{target}->{attachment}->{id} }); } } diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index ceeb9a38a..97c6f2c0e 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -325,13 +325,32 @@ and returning just the ID portion of matching field names. =cut sub validate { - my $user = Bugzilla->user; my ($cgi, $bug_id, $attach_id) = @_; - + + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + my @ids = map(/^flag_type-(\d+)$/ ? $1 : (), $cgi->param()); - foreach my $id (@ids) - { + return unless scalar(@ids); + + # No flag reference should exist when changing several bugs at once. + ThrowCodeError("flags_not_available", { type => 'b' }) unless $bug_id; + + # We don't check that these flag types are valid for + # this bug/attachment. This check will be done later when + # processing new flags, see Flag::FormToNewFlags(). + + # All flag types have to be active + my $inactive_flagtypes = + $dbh->selectrow_array("SELECT 1 FROM flagtypes + WHERE id IN (" . join(',', @ids) . ") + AND is_active = 0 " . + $dbh->sql_limit(1)); + + ThrowCodeError("flag_type_inactive") if $inactive_flagtypes; + + foreach my $id (@ids) { my $status = $cgi->param("flag_type-$id"); # Don't bother validating types the user didn't touch. @@ -353,22 +372,31 @@ sub validate { { id => $id , status => $status }); } + # Make sure the user didn't specify a requestee unless the flag + # is specifically requestable. + my $new_requestee = trim($cgi->param("requestee_type-$id") || ''); + + if ($status eq '?' + && !$flag_type->{is_requesteeble} + && $new_requestee) + { + ThrowCodeError("flag_requestee_disabled", + { name => $flag_type->{name} }); + } + # Make sure the requestee is authorized to access the bug # (and attachment, if this installation is using the "insider group" # feature and the attachment is marked private). if ($status eq '?' && $flag_type->{is_requesteeble} - && trim($cgi->param("requestee_type-$id"))) + && $new_requestee) { - my $requestee_email = trim($cgi->param("requestee_type-$id")); - # We know the requestee exists because we ran # Bugzilla::User::match_field before getting here. - my $requestee = Bugzilla::User->new_from_login($requestee_email); + my $requestee = Bugzilla::User->new_from_login($new_requestee); # Throw an error if the user can't see the bug. - if (!$requestee->can_see_bug($bug_id)) - { + if (!$requestee->can_see_bug($bug_id)) { ThrowUserError("flag_requestee_unauthorized", { flag_type => $flag_type, requestee => $requestee, diff --git a/attachment.cgi b/attachment.cgi index 0c010a061..e4cbe8eed 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -913,8 +913,11 @@ sub insert $vars->{'message'} = 'user_match_multiple'; } - Bugzilla::Flag::validate($cgi, $bugid); - Bugzilla::FlagType::validate($cgi, $bugid, $cgi->param('id')); + # Flag::validate() should not detect any reference to existing + # flags when creating a new attachment. Setting the third param + # to -1 will force this function to check this point. + Bugzilla::Flag::validate($cgi, $bugid, -1); + Bugzilla::FlagType::validate($cgi, $bugid); # Escape characters in strings that will be used in SQL statements. my $sql_filename = SqlQuote($filename); @@ -1148,7 +1151,7 @@ sub update Bugzilla::User::match_field($cgi, { '^requestee(_type)?-(\d+)$' => { 'type' => 'single' } }); - Bugzilla::Flag::validate($cgi, $bugid); + Bugzilla::Flag::validate($cgi, $bugid, $attach_id); Bugzilla::FlagType::validate($cgi, $bugid, $attach_id); # Lock database tables in preparation for updating the attachment. diff --git a/process_bug.cgi b/process_bug.cgi index 1fa8400e9..4b6410b2c 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -165,12 +165,11 @@ foreach my $field ("dependson", "blocked") { 'assigned_to' => { 'type' => 'single' }, '^requestee(_type)?-(\d+)$' => { 'type' => 'single' }, }); -# Validate flags, but only if the user is changing a single bug, -# since the multi-change form doesn't include flag changes. -if (defined $cgi->param('id')) { - Bugzilla::Flag::validate($cgi, $cgi->param('id')); - Bugzilla::FlagType::validate($cgi, $cgi->param('id')); -} + +# Validate flags in all cases. validate() should not detect any +# reference to flags if $cgi->param('id') is undefined. +Bugzilla::Flag::validate($cgi, $cgi->param('id')); +Bugzilla::FlagType::validate($cgi, $cgi->param('id')); ###################################################################### # End Data/Security Validation diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index fd3f8fb20..36a752949 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -135,6 +135,15 @@ [% title = "Invalid Dimensions" %] The width or height specified is not a positive integer. + [% ELSIF error == "invalid_flag_association" %] + [% title = "Invalid Flag Association" %] + Some flags do not belong to + [% IF attach_id %] + attachment [% attach_id FILTER html %]. + [% ELSE %] + [%+ terms.bug %] [%+ bug_id FILTER html %]. + [% END %] + [% ELSIF error == "invalid_isactive_flag" %] [% title = "Invalid isactive flag" %] The active flag was improperly set. There may be @@ -153,6 +162,20 @@ [% ELSIF error == "flag_nonexistent" %] There is no flag with ID #[% id FILTER html %]. + + [% ELSIF error == "flags_not_available" %] + [% title = "Flag Editing not Allowed" %] + [% IF type == "b" %] + Flags cannot be set or changed when + changing several [% terms.bugs %] at once. + [% ELSE %] + References to existing flags when creating + a new attachment are invalid. + [% END %] + + [% ELSIF error == "flag_requestee_disabled" %] + [% title = "Flag not Specifically Requestable" %] + The flag [% name FILTER html %] is not specifically requestable. [% ELSIF error == "flag_status_invalid" %] The flag status [% status FILTER html %] @@ -172,6 +195,10 @@ The flag type ID [% id FILTER html %] is not a positive integer. + [% ELSIF error == "flag_type_inactive" %] + [% title = "Inactive Flag Types" %] + Some flag types are inactive and cannot be used to create new flags. + [% ELSIF error == "flag_type_nonexistent" %] There is no flag type with the ID [% id FILTER html %]. -- cgit v1.2.3-65-gdbad From d79c2c86ef0b69c4250642e3709f22a34fe7c8a0 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Sat, 30 Jul 2005 07:46:39 +0000 Subject: Fix for bug 302083: automatically converts BMP files to PNG files to conserve disk space; patch by Greg Hendricks; r=myk, a=myk --- attachment.cgi | 22 ++++++++++++++++++++-- checksetup.pl | 9 +++++++++ defparams.pl | 19 +++++++++++++++++++ template/en/default/attachment/created.html.tmpl | 7 ++++++- 4 files changed, 54 insertions(+), 3 deletions(-) (limited to 'attachment.cgi') diff --git a/attachment.cgi b/attachment.cgi index e4cbe8eed..1ed7a2322 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -24,6 +24,7 @@ # Dave Miller # Alexander J. Vincent # Max Kanat-Alexander +# Greg Hendricks ################################################################################ # Script Initialization @@ -335,7 +336,22 @@ sub validateData $data || ($cgi->param('bigfile')) || ThrowUserError("zero_length_file"); - + + # Windows screenshots are usually uncompressed BMP files which + # makes for a quick way to eat up disk space. Let's compress them. + # We do this before we check the size since the uncompressed version + # could easily be greater than maxattachmentsize. + if (Param('convert_uncompressed_images') && $cgi->param('contenttype') eq 'image/bmp'){ + require Image::Magick; + my $img = Image::Magick->new(magick=>'bmp'); + $img->BlobToImage($data); + $img->set(magick=>'png'); + my $imgdata = $img->ImageToBlob(); + $data = $imgdata; + $cgi->param('contenttype', 'image/png'); + $vars->{'convertedbmp'} = 1; + } + # Make sure the attachment does not exceed the maximum permitted size my $len = $data ? length($data) : 0; if ($maxsize && $len > $maxsize) { @@ -891,9 +907,11 @@ sub insert ValidateComment(scalar $cgi->param('comment')); my $filename = validateFilename(); validateIsPatch(); - my $data = validateData(); validateDescription(); + # need to validate content type before data as + # we now check the content type for image/bmp in validateData() validateContentType() unless $cgi->param('ispatch'); + my $data = validateData(); my @obsolete_ids = (); @obsolete_ids = validateObsolete() if $cgi->param('obsolete'); diff --git a/checksetup.pl b/checksetup.pl index 7e5152628..f3a332870 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -368,6 +368,7 @@ my $xmlparser = have_vers("XML::Parser",0); my $gdgraph = have_vers("GD::Graph",0); my $gdtextalign = have_vers("GD::Text::Align",0); my $patchreader = have_vers("PatchReader","0.9.4"); +my $imagemagick = have_vers("Image::Magick",0); print "\n" unless $silent; @@ -392,6 +393,14 @@ if (!$xmlparser && !$silent) { "the XML::Parser module by running (as $::root):\n\n", " " . install_command("XML::Parser") . "\n\n"; } +if (!$imagemagick && !$silent) { + print "If you want to convert BMP image attachments to PNG to conserve\n", + "disk space, you will need to install the ImageMagick application\n ", + "Available from http://www.imagemagick.org, and the Image::Magick", + "Perl module by running (as $::root):\n\n", + " " . install_command("Image::Magick") . "\n\n"; + +} if ((!$gd || !$gdgraph || !$gdtextalign) && !$silent) { print "If you you want to see graphical bug reports (bar, pie and line "; print "charts of \ncurrent data), you should install libgd and the "; diff --git a/defparams.pl b/defparams.pl index 09f2a5012..49448b9d9 100644 --- a/defparams.pl +++ b/defparams.pl @@ -209,6 +209,15 @@ sub check_user_verify_class { return ""; } +sub check_image_converter { + my ($value, $hash) = @_; + if ($value == 1){ + eval "require Image::Magick"; + return "Error requiring Image::Magick: '$@'" if $@; + } + return ""; +} + sub check_languages { my @languages = split /[,\s]+/, trim($_[0]); if(!scalar(@languages)) { @@ -1291,6 +1300,16 @@ Reason: %reason% default => '0', checker => \&check_numeric }, + + { + name => 'convert_uncompressed_images', + desc => 'If this option is on, attachments with content type image/bmp ' . + 'will be converted to image/png and compressed before uploading to'. + 'the database to conserve disk space.', + type => 'b', + default => 0, + checker => \&check_image_converter + }, { name => 'chartgroup', diff --git a/template/en/default/attachment/created.html.tmpl b/template/en/default/attachment/created.html.tmpl index 9bfb36caf..ef318f575 100644 --- a/template/en/default/attachment/created.html.tmpl +++ b/template/en/default/attachment/created.html.tmpl @@ -44,7 +44,12 @@ [% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = bugid %] - + [% IF convertedbmp %] +

+ Note: [% terms.Bugzilla %] automatically converted your BMP image file to a + compressed PNG format. +

+ [% END %] [% IF contenttypemethod == 'autodetect' %]

Note: [% terms.Bugzilla %] automatically detected the content type -- cgit v1.2.3-65-gdbad From 8d06d1ef539f3f8becc56953b040bc710ec9a859 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Wed, 10 Aug 2005 08:30:39 +0000 Subject: Bug 301508: Remove CGI.pl - Patch by Frédéric Buclin r=mkanat,wicked a=justdave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Attachment.pm | 4 +- Bugzilla/Flag.pm | 5 +- Bugzilla/FlagType.pm | 3 +- Bugzilla/Search.pm | 2 +- Bugzilla/Token.pm | 4 +- CGI.pl | 67 ---------------------- attachment.cgi | 26 ++++----- buglist.cgi | 23 ++++---- chart.cgi | 9 ++- colchange.cgi | 5 +- config.cgi | 2 +- contrib/sendunsentbugmail.pl | 2 +- contrib/syncLDAP.pl | 2 +- createaccount.cgi | 2 +- describecomponents.cgi | 2 +- describekeywords.cgi | 2 +- doeditparams.cgi | 2 +- duplicates.cgi | 1 - editcomponents.cgi | 2 +- editflagtypes.cgi | 2 +- editgroups.cgi | 2 +- editkeywords.cgi | 2 +- editmilestones.cgi | 1 - editparams.cgi | 2 +- editproducts.cgi | 1 - editsettings.cgi | 2 +- editusers.cgi | 1 - editversions.cgi | 1 - editwhines.cgi | 1 - enter_bug.cgi | 2 +- globals.pl | 1 + importxml.pl | 1 - index.cgi | 6 +- long_list.cgi | 5 +- move.pl | 2 +- page.cgi | 2 +- post_bug.cgi | 7 +-- process_bug.cgi | 2 +- query.cgi | 8 +-- quips.cgi | 8 +-- relogin.cgi | 8 +-- report.cgi | 9 +-- reports.cgi | 2 - request.cgi | 2 +- sanitycheck.cgi | 2 +- show_activity.cgi | 4 +- show_bug.cgi | 2 +- showdependencygraph.cgi | 2 +- showdependencytree.cgi | 2 +- sidebar.cgi | 7 +-- summarize_time.cgi | 2 +- template/en/default/global/help-header.html.tmpl | 2 +- template/en/default/global/help.html.tmpl | 2 +- .../en/default/search/search-advanced.html.tmpl | 4 +- token.cgi | 2 +- userprefs.cgi | 2 +- votes.cgi | 2 +- xml.cgi | 5 +- 58 files changed, 97 insertions(+), 188 deletions(-) delete mode 100644 CGI.pl (limited to 'attachment.cgi') diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 10fa6ddfc..78f4ceed5 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -28,8 +28,8 @@ use strict; package Bugzilla::Attachment; -# This module requires that its caller have said "require CGI.pl" to import -# relevant functions from that script and its companion globals.pl. +# This module requires that its caller have said "require globals.pl" to import +# relevant functions from that script. # Use the Flag module to handle flags. use Bugzilla::Flag; diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 65636d78c..6c5adf440 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -37,12 +37,11 @@ See below for more information. =item * Prior to calling routines in this module, it's assumed that you have -already done a C. This will eventually change in a -future version when CGI.pl is removed. +already done a C. =item * -Import relevant functions from that script and its companion globals.pl. +Import relevant functions from that script. =item * diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index 49c9f777e..678721b5f 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -35,8 +35,7 @@ See below for more information. =item * Prior to calling routines in this module, it's assumed that you have -already done a C. This will eventually change in a -future version when CGI.pl is removed. +already done a C. =item * diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 31dbcd2ab..0b1ac94ba 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -29,7 +29,7 @@ use strict; -# The caller MUST require CGI.pl and globals.pl before using this +# The caller MUST require globals.pl before using this module. use vars qw($userid); diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index fe72915a3..44ee1a642 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -37,8 +37,8 @@ use Bugzilla::Util; use Date::Format; use Date::Parse; -# This module requires that its caller have said "require CGI.pl" to import -# relevant functions from that script and its companion globals.pl. +# This module requires that its caller have said "require globals.pl" to import +# relevant functions from that script. ################################################################################ # Constants diff --git a/CGI.pl b/CGI.pl deleted file mode 100644 index c1b8aca11..000000000 --- a/CGI.pl +++ /dev/null @@ -1,67 +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 -# Dan Mosedale -# Joe Robins -# Dave Miller -# Christopher Aillon -# Gervase Markham -# Christian Reis - -# Contains some global routines used throughout the CGI scripts of Bugzilla. - -use strict; -use lib "."; - -# use Carp; # for confess - -use Bugzilla::Util; -use Bugzilla::Config; -use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::BugMail; -use Bugzilla::Bug; -use Bugzilla::User; - -# Shut up misguided -w warnings about "used only once". For some reason, -# "use vars" chokes on me when I try it here. - -sub CGI_pl_sillyness { - my $zz; - $zz = $::buffer; -} - -require 'globals.pl'; - -use vars qw($vars); - -############# Live code below here (that is, not subroutine defs) ############# - -use Bugzilla; - -# XXX - mod_perl - reset this between runs -$::cgi = Bugzilla->cgi; - -$::buffer = $::cgi->query_string(); - -# This could be needed in any CGI, so we set it here. -$vars->{'help'} = $::cgi->param('help') ? 1 : 0; - -1; diff --git a/attachment.cgi b/attachment.cgi index 1ed7a2322..ea04e2c19 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -35,13 +35,10 @@ use strict; use lib qw(.); -use vars qw( - $template - $vars -); +use vars qw($template $vars); # Include the Bugzilla CGI and general utility library. -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Config qw(:locations); # Use these modules to handle flags. @@ -135,7 +132,7 @@ sub validateID # Happens when calling plain attachment.cgi from the urlbar directly if ($param eq 'id' && !$cgi->param('id')) { - print Bugzilla->cgi->header(); + print $cgi->header(); $template->process("attachment/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -512,9 +509,9 @@ sub view $filename =~ s/\\/\\\\/g; # escape backslashes $filename =~ s/"/\\"/g; # escape quotes - print Bugzilla->cgi->header(-type=>"$contenttype; name=\"$filename\"", - -content_disposition=> "inline; filename=\"$filename\"", - -content_length => $filesize); + print $cgi->header(-type=>"$contenttype; name=\"$filename\"", + -content_disposition=> "inline; filename=\"$filename\"", + -content_length => $filesize); if ($thedata) { print $thedata; @@ -746,7 +743,6 @@ sub diff require PatchReader::DiffPrinter::raw; $last_reader->sends_data_to(new PatchReader::DiffPrinter::raw()); # Actually print out the patch - use vars qw($cgi); print $cgi->header(-type => 'text/plain', -expires => '+3M'); $reader->iterate_string("Attachment $attach_id", $thedata); @@ -830,7 +826,7 @@ sub viewall $vars->{'bugsummary'} = $bugsummary; $vars->{'GetBugLink'} = \&GetBugLink; - print Bugzilla->cgi->header(); + print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/show-multiple.html.tmpl", $vars) @@ -887,7 +883,7 @@ sub enter $vars->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types); - print Bugzilla->cgi->header(); + print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/create.html.tmpl", $vars) @@ -1070,7 +1066,7 @@ sub insert $vars->{'contenttypemethod'} = $cgi->param('contenttypemethod'); $vars->{'contenttype'} = $cgi->param('contenttype'); - print Bugzilla->cgi->header(); + print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/created.html.tmpl", $vars) @@ -1135,7 +1131,7 @@ sub edit require PatchReader; $vars->{'patchviewerinstalled'} = 1; }; - print Bugzilla->cgi->header(); + print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/edit.html.tmpl", $vars) @@ -1290,7 +1286,7 @@ sub update $vars->{'attachid'} = $attach_id; $vars->{'bugid'} = $bugid; - print Bugzilla->cgi->header(); + print $cgi->header(); # Generate and return the UI (HTML page) from the appropriate template. $template->process("attachment/updated.html.tmpl", $vars) diff --git a/buglist.cgi b/buglist.cgi index 45c0db552..6bc3af291 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -42,7 +42,7 @@ use Bugzilla::Constants; use Bugzilla::User; # Include the Bugzilla CGI and general utility library. -require "CGI.pl"; +require "globals.pl"; use vars qw($db_name @components @@ -58,8 +58,9 @@ use vars qw($db_name my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; +my $buffer = $cgi->query_string(); -if (length($::buffer) == 0) { +if (length($buffer) == 0) { print $cgi->header(-refresh=> '10; URL=query.cgi'); ThrowUserError("buglist_parameters_required"); } @@ -146,8 +147,8 @@ if (defined $cgi->param('regetlastlist')) { }); } -if ($::buffer =~ /&cmd-/) { - my $url = "query.cgi?$::buffer#chart"; +if ($buffer =~ /&cmd-/) { + my $url = "query.cgi?$buffer#chart"; print $cgi->redirect(-location => $url); # Generate and return the UI (HTML page) from the appropriate template. $vars->{'message'} = "buglist_adding_field"; @@ -361,18 +362,18 @@ if ($cgi->param('cmdtype') eq "dorem" && $cgi->param('remaction') =~ /^run/) { # Take appropriate action based on user's request. if ($cgi->param('cmdtype') eq "dorem") { if ($cgi->param('remaction') eq "run") { - $::buffer = LookupNamedQuery(scalar $cgi->param("namedcmd")); + $buffer = LookupNamedQuery(scalar $cgi->param("namedcmd")); $vars->{'searchname'} = $cgi->param('namedcmd'); $vars->{'searchtype'} = "saved"; - $params = new Bugzilla::CGI($::buffer); + $params = new Bugzilla::CGI($buffer); $order = $params->param('order') || $order; } elsif ($cgi->param('remaction') eq "runseries") { - $::buffer = LookupSeries(scalar $cgi->param("series_id")); + $buffer = LookupSeries(scalar $cgi->param("series_id")); $vars->{'searchname'} = $cgi->param('namedcmd'); $vars->{'searchtype'} = "series"; - $params = new Bugzilla::CGI($::buffer); + $params = new Bugzilla::CGI($buffer); $order = $params->param('order') || $order; } elsif ($cgi->param('remaction') eq "forget") { @@ -402,7 +403,7 @@ if ($cgi->param('cmdtype') eq "dorem") { elsif (($cgi->param('cmdtype') eq "doit") && defined $cgi->param('remtype')) { if ($cgi->param('remtype') eq "asdefault") { Bugzilla->login(LOGIN_REQUIRED); - InsertNamedQuery(Bugzilla->user->id, DEFAULT_QUERY_NAME, $::buffer); + InsertNamedQuery(Bugzilla->user->id, DEFAULT_QUERY_NAME, $buffer); $vars->{'message'} = "buglist_new_default_query"; } elsif ($cgi->param('remtype') eq "asnamed") { @@ -439,7 +440,7 @@ elsif (($cgi->param('cmdtype') eq "doit") && defined $cgi->param('remtype')) { # form - see bug 252295 if (!$params->param('query_format')) { $params->param('query_format', 'advanced'); - $::buffer = $params->query_string; + $buffer = $params->query_string; } ################################################################################ @@ -937,7 +938,7 @@ $vars->{'closedstates'} = ['CLOSED', 'VERIFIED', 'RESOLVED']; # buffer that was created when we initially parsed the URL on script startup, # then we remove all non-query fields from it, f.e. the sort order (order) # and command type (cmdtype) fields. -$vars->{'urlquerypart'} = $::buffer; +$vars->{'urlquerypart'} = $buffer; $vars->{'urlquerypart'} =~ s/(order|cmdtype)=[^&]*&?//g; $vars->{'order'} = $order; diff --git a/chart.cgi b/chart.cgi index bc25d52e9..31f961cac 100755 --- a/chart.cgi +++ b/chart.cgi @@ -44,13 +44,17 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; +use Bugzilla; use Bugzilla::Constants; use Bugzilla::Chart; use Bugzilla::Series; use Bugzilla::User; -use vars qw($cgi $template $vars); +use vars qw($vars); + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; # Go back to query.cgi if we are adding a boolean chart parameter. if (grep(/^cmd-/, $cgi->param())) { @@ -60,7 +64,6 @@ if (grep(/^cmd-/, $cgi->param())) { exit; } -my $template = Bugzilla->template; my $action = $cgi->param('action'); my $series_id = $cgi->param('series_id'); diff --git a/colchange.cgi b/colchange.cgi index 30103406f..e99b81b70 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -27,7 +27,6 @@ use lib qw(.); use vars qw( @legal_keywords - $buffer $template $vars ); @@ -35,7 +34,7 @@ use vars qw( use Bugzilla; use Bugzilla::Constants; use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; Bugzilla->login(); @@ -150,7 +149,7 @@ if (defined $cgi->cookie('COLUMNLIST')) { $vars->{'collist'} = \@collist; $vars->{'splitheader'} = $cgi->cookie('SPLITHEADER') ? 1 : 0; -$vars->{'buffer'} = $::buffer; +$vars->{'buffer'} = $cgi->query_string(); # Generate and return the UI (HTML page) from the appropriate template. print $cgi->header(); diff --git a/config.cgi b/config.cgi index 1306c0b66..bbffe20d8 100755 --- a/config.cgi +++ b/config.cgi @@ -31,7 +31,7 @@ use strict; # Include the Bugzilla CGI and general utility library. use lib qw(.); -require "CGI.pl"; +require "globals.pl"; # Retrieve this installation's configuration. GetVersionTable(); diff --git a/contrib/sendunsentbugmail.pl b/contrib/sendunsentbugmail.pl index c82c0ae30..9453a6538 100644 --- a/contrib/sendunsentbugmail.pl +++ b/contrib/sendunsentbugmail.pl @@ -25,7 +25,7 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Constants; use Bugzilla::BugMail; diff --git a/contrib/syncLDAP.pl b/contrib/syncLDAP.pl index 14ba1402c..6e78854e0 100755 --- a/contrib/syncLDAP.pl +++ b/contrib/syncLDAP.pl @@ -23,7 +23,7 @@ use strict; -require "CGI.pl"; +require "globals.pl"; use lib qw(.); diff --git a/createaccount.cgi b/createaccount.cgi index 337f89d0a..29b3c00ec 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -28,7 +28,7 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla; use Bugzilla::Constants; diff --git a/describecomponents.cgi b/describecomponents.cgi index 8e1755498..9602c0fe0 100755 --- a/describecomponents.cgi +++ b/describecomponents.cgi @@ -26,7 +26,7 @@ use lib qw(.); use Bugzilla; use Bugzilla::Constants; -require "CGI.pl"; +require "globals.pl"; use vars qw($vars @legal_product); diff --git a/describekeywords.cgi b/describekeywords.cgi index e7b3b759b..ab88c2f15 100755 --- a/describekeywords.cgi +++ b/describekeywords.cgi @@ -27,7 +27,7 @@ use lib "."; use Bugzilla; use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; # Use the global template variables. use vars qw($vars $template); diff --git a/doeditparams.cgi b/doeditparams.cgi index 431aa91c2..cfc21e23d 100755 --- a/doeditparams.cgi +++ b/doeditparams.cgi @@ -30,7 +30,7 @@ use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT :admin $datadir); use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; Bugzilla->login(LOGIN_REQUIRED); diff --git a/duplicates.cgi b/duplicates.cgi index 744ad8147..92c697f53 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -30,7 +30,6 @@ use AnyDBM_File; use lib qw(.); require "globals.pl"; -require "CGI.pl"; use Bugzilla; use Bugzilla::Search; diff --git a/editcomponents.cgi b/editcomponents.cgi index 6f8bc99f2..c25f971fc 100755 --- a/editcomponents.cgi +++ b/editcomponents.cgi @@ -28,7 +28,6 @@ use strict; use lib "."; -require "CGI.pl"; require "globals.pl"; use Bugzilla::Constants; @@ -36,6 +35,7 @@ use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Series; use Bugzilla::Util; use Bugzilla::User; +use Bugzilla::Bug; use vars qw($template $vars); diff --git a/editflagtypes.cgi b/editflagtypes.cgi index 57795f493..ec449a87d 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -29,7 +29,7 @@ use strict; use lib "."; # Include the Bugzilla CGI and general utility library. -require "CGI.pl"; +require "globals.pl"; # Use Bugzilla's flag modules for handling flag types. use Bugzilla; diff --git a/editgroups.cgi b/editgroups.cgi index 5e74163da..e41dc7d07 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -32,7 +32,7 @@ use lib "."; use Bugzilla; use Bugzilla::Constants; use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; diff --git a/editkeywords.cgi b/editkeywords.cgi index 8ad74710e..3062e3ded 100755 --- a/editkeywords.cgi +++ b/editkeywords.cgi @@ -23,7 +23,7 @@ use strict; use lib "."; -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); diff --git a/editmilestones.cgi b/editmilestones.cgi index 4fbcab046..fa1117ede 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -19,7 +19,6 @@ use strict; use lib "."; -require "CGI.pl"; require "globals.pl"; use Bugzilla::Constants; diff --git a/editparams.cgi b/editparams.cgi index 668a13801..264e991a4 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -29,7 +29,7 @@ use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT :admin); use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; Bugzilla->login(LOGIN_REQUIRED); diff --git a/editproducts.cgi b/editproducts.cgi index 18e845efc..cacf8eef8 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -33,7 +33,6 @@ use strict; use lib "."; use vars qw ($template $vars); use Bugzilla::Constants; -require "CGI.pl"; require "globals.pl"; use Bugzilla::Bug; use Bugzilla::Series; diff --git a/editsettings.cgi b/editsettings.cgi index 80a8921d5..742bd7176 100755 --- a/editsettings.cgi +++ b/editsettings.cgi @@ -24,7 +24,7 @@ use Bugzilla::Constants; use Bugzilla::User; use Bugzilla::User::Setting; -require "CGI.pl"; +require "globals.pl"; # Use global template variables. use vars qw($template $vars); diff --git a/editusers.cgi b/editusers.cgi index 9da66424d..f628752cd 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -18,7 +18,6 @@ use strict; use lib "."; -require "CGI.pl"; require "globals.pl"; use vars qw( $vars ); diff --git a/editversions.cgi b/editversions.cgi index a7ced7df3..cf2303e5f 100755 --- a/editversions.cgi +++ b/editversions.cgi @@ -31,7 +31,6 @@ use strict; use lib "."; -require "CGI.pl"; require "globals.pl"; use Bugzilla::Constants; diff --git a/editwhines.cgi b/editwhines.cgi index 1f5c954f3..66387dd82 100755 --- a/editwhines.cgi +++ b/editwhines.cgi @@ -28,7 +28,6 @@ use strict; use lib "."; -require "CGI.pl"; require "globals.pl"; use vars qw( $vars ); diff --git a/enter_bug.cgi b/enter_bug.cgi index f3437b7ed..1d85761b4 100755 --- a/enter_bug.cgi +++ b/enter_bug.cgi @@ -41,7 +41,7 @@ use Bugzilla; use Bugzilla::Constants; use Bugzilla::Bug; use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; use vars qw( $template diff --git a/globals.pl b/globals.pl index f8f070366..d4ba17004 100644 --- a/globals.pl +++ b/globals.pl @@ -37,6 +37,7 @@ use Bugzilla::Util; use Bugzilla::Config qw(:DEFAULT ChmodDataFile $localconfig $datadir); use Bugzilla::BugMail; use Bugzilla::User; +use Bugzilla::Error; # Shut up misguided -w warnings about "used only once". For some reason, # "use vars" chokes on me when I try it here. diff --git a/importxml.pl b/importxml.pl index 0d9dcb868..9c56b3d1a 100755 --- a/importxml.pl +++ b/importxml.pl @@ -69,7 +69,6 @@ $Data::Dumper::Useqq = 1; use Bugzilla::BugMail; use Bugzilla::User; -require "CGI.pl"; require "globals.pl"; GetVersionTable(); diff --git a/index.cgi b/index.cgi index 88393b417..bc3a1272f 100755 --- a/index.cgi +++ b/index.cgi @@ -30,11 +30,9 @@ use strict; # Include the Bugzilla CGI and general utility library. use lib "."; -require "CGI.pl"; +require "globals.pl"; -use vars qw( - $vars -); +use vars qw($vars); # Check whether or not the user is logged in and, if so, set the $::userid use Bugzilla::Constants; diff --git a/long_list.cgi b/long_list.cgi index 04758729d..a5d59909b 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -23,10 +23,9 @@ use strict; use lib qw(.); +use Bugzilla; -require "CGI.pl"; - -our $cgi; +my $cgi = Bugzilla->cgi; # Convert comma/space separated elements into separate params my @ids = (); diff --git a/move.pl b/move.pl index 92a051b63..82c40034d 100755 --- a/move.pl +++ b/move.pl @@ -26,7 +26,7 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use vars qw($template $userid); diff --git a/page.cgi b/page.cgi index 6e78317fc..9954c64a8 100755 --- a/page.cgi +++ b/page.cgi @@ -34,7 +34,7 @@ use lib "."; use Bugzilla; -require "CGI.pl"; +require "globals.pl"; use vars qw($template $vars); diff --git a/post_bug.cgi b/post_bug.cgi index 01c0e1845..0c421b638 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -26,7 +26,7 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla; use Bugzilla::Constants; use Bugzilla::Bug; @@ -37,7 +37,6 @@ use Bugzilla::Field; # "use vars" chokes on me when I try it here. sub sillyness { my $zz; - $zz = $::buffer; $zz = %::components; $zz = %::versions; $zz = @::legal_opsys; @@ -52,9 +51,7 @@ sub sillyness { use vars qw($vars $template); my $user = Bugzilla->login(LOGIN_REQUIRED); - my $cgi = Bugzilla->cgi; - my $dbh = Bugzilla->dbh; # do a match on the fields if applicable @@ -94,7 +91,7 @@ if (defined $cgi->param('product')) { } if (defined $cgi->param('maketemplate')) { - $vars->{'url'} = $::buffer; + $vars->{'url'} = $cgi->query_string(); print $cgi->header(); $template->process("bug/create/make-template.html.tmpl", $vars) diff --git a/process_bug.cgi b/process_bug.cgi index 9a3db0a8f..c624eff3a 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -46,7 +46,7 @@ my $PrivilegesRequired = 0; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla; use Bugzilla::Constants; use Bugzilla::Bug; diff --git a/query.cgi b/query.cgi index ff04c15a0..a25f8535d 100755 --- a/query.cgi +++ b/query.cgi @@ -28,7 +28,7 @@ use strict; use lib "."; -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Constants; use Bugzilla::Search; @@ -36,7 +36,6 @@ use Bugzilla::User; use Bugzilla::Util; use vars qw( - @CheckOptionValues @legal_resolution @legal_bug_status @legal_components @@ -57,6 +56,7 @@ use vars qw( my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; +my $buffer = $cgi->query_string(); if ($cgi->param("GoAheadAndLogIn")) { # We got here from a login page, probably from relogin.cgi. We better @@ -112,7 +112,7 @@ if ($cgi->param('nukedefaultquery')) { " WHERE userid = ? AND name = ?", undef, ($userid, DEFAULT_QUERY_NAME)); } - $::buffer = ""; + $buffer = ""; } my $userdefaultquery; @@ -200,7 +200,7 @@ sub PrefillForm { } -if (!PrefillForm($::buffer)) { +if (!PrefillForm($buffer)) { # Ah-hah, there was no form stuff specified. Do it again with the # default query. if ($userdefaultquery) { diff --git a/quips.cgi b/quips.cgi index d811ee5fe..364f51448 100755 --- a/quips.cgi +++ b/quips.cgi @@ -25,15 +25,11 @@ use strict; -use vars qw( - $userid - $template - $vars -); +use vars qw($userid $template $vars); use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Constants; diff --git a/relogin.cgi b/relogin.cgi index 6843405c2..f743eb8f3 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -23,11 +23,9 @@ use strict; -use vars qw($template $vars); - use lib qw(.); - -require "CGI.pl"; +use Bugzilla; +use Bugzilla::Error; # We don't want to remove a random logincookie from the db, so # call Bugzilla->login(). If we're logged in after this, then @@ -36,9 +34,11 @@ Bugzilla->login(); Bugzilla->logout(); +my $template = Bugzilla->template; my $cgi = Bugzilla->cgi; print $cgi->header(); +my $vars = {}; $vars->{'message'} = "logged_out"; $template->process("global/message.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/report.cgi b/report.cgi index 30c7cade8..e7b94ffc2 100755 --- a/report.cgi +++ b/report.cgi @@ -24,7 +24,7 @@ use strict; use lib "."; -require "CGI.pl"; +require "globals.pl"; use vars qw($template $vars @legal_opsys @legal_platform @legal_severity); @@ -32,6 +32,7 @@ use Bugzilla; use Bugzilla::Constants; my $cgi = Bugzilla->cgi; +my $buffer = $cgi->query_string(); # Go straight back to query.cgi if we are adding a boolean chart. if (grep(/^cmd-/, $cgi->param())) { @@ -266,9 +267,9 @@ if ($action eq "wrap") { # We need to keep track of the defined restrictions on each of the # axes, because buglistbase, below, throws them away. Without this, we # get buglistlinks wrong if there is a restriction on an axis field. - $vars->{'col_vals'} = join("&", $::buffer =~ /[&?]($col_field=[^&]+)/g); - $vars->{'row_vals'} = join("&", $::buffer =~ /[&?]($row_field=[^&]+)/g); - $vars->{'tbl_vals'} = join("&", $::buffer =~ /[&?]($tbl_field=[^&]+)/g); + $vars->{'col_vals'} = join("&", $buffer =~ /[&?]($col_field=[^&]+)/g); + $vars->{'row_vals'} = join("&", $buffer =~ /[&?]($row_field=[^&]+)/g); + $vars->{'tbl_vals'} = join("&", $buffer =~ /[&?]($tbl_field=[^&]+)/g); # We need a number of different variants of the base URL for different # URLs in the HTML. diff --git a/reports.cgi b/reports.cgi index e8191a0d0..49948e2bb 100755 --- a/reports.cgi +++ b/reports.cgi @@ -39,8 +39,6 @@ use lib qw(.); use Bugzilla::Config qw(:DEFAULT $datadir); -require "CGI.pl"; - require "globals.pl"; use vars qw(@legal_product); # globals from er, globals.pl diff --git a/request.cgi b/request.cgi index 4c6e7600f..a3779bd8d 100755 --- a/request.cgi +++ b/request.cgi @@ -29,7 +29,7 @@ use strict; # Include the Bugzilla CGI and general utility library. use lib qw(.); -require "CGI.pl"; +require "globals.pl"; # Use Bugzilla's Request module which contains utilities for handling requests. use Bugzilla::Flag; diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 00a7ef0c5..1035a8d2e 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -27,7 +27,7 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Constants; use Bugzilla::User; diff --git a/show_activity.cgi b/show_activity.cgi index eacaeba37..b0ad42379 100755 --- a/show_activity.cgi +++ b/show_activity.cgi @@ -27,7 +27,7 @@ use strict; use lib qw(.); use vars qw ($template $vars); -require "CGI.pl"; +require "globals.pl"; use Bugzilla::Bug; @@ -54,7 +54,7 @@ ValidateBugID($bug_id); $vars->{'bug_id'} = $bug_id; -print Bugzilla->cgi->header(); +print $cgi->header(); $template->process("bug/activity/show.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/show_bug.cgi b/show_bug.cgi index e855b442f..c45a7cd99 100755 --- a/show_bug.cgi +++ b/show_bug.cgi @@ -28,7 +28,7 @@ use Bugzilla; use Bugzilla::Constants; use Bugzilla::User; -require "CGI.pl"; +require "globals.pl"; use vars qw($template $vars $userid); diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index 8f0ff5dfa..4339bb3e5 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -32,7 +32,7 @@ use Bugzilla::Util; use Bugzilla::BugMail; use Bugzilla::Bug; -require "CGI.pl"; +require "globals.pl"; Bugzilla->login(); diff --git a/showdependencytree.cgi b/showdependencytree.cgi index e473357d1..7c3b99465 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -26,7 +26,7 @@ use strict; use lib qw(.); -require "CGI.pl"; +require "globals.pl"; use Bugzilla::User; use Bugzilla::Bug; diff --git a/sidebar.cgi b/sidebar.cgi index 73a22d1b3..15506eba4 100755 --- a/sidebar.cgi +++ b/sidebar.cgi @@ -18,13 +18,10 @@ use strict; use lib "."; -require "CGI.pl"; +require "globals.pl"; # Shut up "Used Only Once" errors -use vars qw( - $template - $vars -); +use vars qw($template $vars); Bugzilla->login(); diff --git a/summarize_time.cgi b/summarize_time.cgi index eb3aff230..31aedd9e1 100755 --- a/summarize_time.cgi +++ b/summarize_time.cgi @@ -27,7 +27,7 @@ use Bugzilla::Bug; # EmitDependList use Bugzilla::Util; # trim use Bugzilla::Constants; # LOGIN_* use Bugzilla::User; # UserInGroup -require "CGI.pl"; +require "globals.pl"; GetVersionTable(); diff --git a/template/en/default/global/help-header.html.tmpl b/template/en/default/global/help-header.html.tmpl index 330ba9160..96814e93f 100644 --- a/template/en/default/global/help-header.html.tmpl +++ b/template/en/default/global/help-header.html.tmpl @@ -22,7 +22,7 @@ [% USE Bugzilla %] [% cgi = Bugzilla.cgi %] -[% IF help %] +[% IF cgi.param("help") %] [% IF cgi.user_agent("Mozilla/5") %]