allow users to watch the bugs of other users
git-svn-id: svn://10.0.0.236/trunk@64387 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
211
mozilla/webtools/bugzilla/Bugzilla/RelationSet.pm
Normal file
211
mozilla/webtools/bugzilla/Bugzilla/RelationSet.pm
Normal file
@@ -0,0 +1,211 @@
|
||||
#
|
||||
# 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) 2000 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dan Mosedale <dmose@mozilla.org>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
|
||||
# This object models a set of relations between one item and a group
|
||||
# of other items. An example is the set of relations between one bug
|
||||
# and the users CCed on that bug. Currently, the relation objects are
|
||||
# expected to be bugzilla userids. However, this could and perhaps
|
||||
# should be generalized to work with non userid objects, such as
|
||||
# keywords associated with a bug. That shouldn't be hard to do; it
|
||||
# might involve turning this into a virtual base class, and having
|
||||
# UserSet and KeywordSet types that inherit from it.
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
require "globals.pl";
|
||||
|
||||
package RelationSet;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
|
||||
# create a new empty RelationSet
|
||||
#
|
||||
sub new {
|
||||
my $type = shift();
|
||||
|
||||
# create a ref to an empty hash and bless it
|
||||
#
|
||||
my $self = {};
|
||||
bless $self, $type;
|
||||
|
||||
# construct from a comma-delimited string
|
||||
#
|
||||
if ($#_ == 0) {
|
||||
$self->mergeFromString($_[0]);
|
||||
}
|
||||
# unless this was a constructor for an empty list, somebody screwed up.
|
||||
#
|
||||
elsif ( $#_ != -1 ) {
|
||||
confess("invalid number of arguments");
|
||||
}
|
||||
|
||||
# bless as a RelationSet
|
||||
#
|
||||
return $self;
|
||||
}
|
||||
|
||||
# Assumes that the set of relations "FROM $table WHERE $constantSql and
|
||||
# $column = $value" is currently represented by $self, and this set should
|
||||
# be updated to look like $other.
|
||||
#
|
||||
# Returns an array of two strings, one INSERT and one DELETE, which will
|
||||
# make this change. Either or both strings may be the empty string,
|
||||
# meaning that no INSERT or DELETE or both (respectively) need to be done.
|
||||
#
|
||||
# THE CALLER IS RESPONSIBLE FOR ANY DESIRED LOCKING AND/OR CONSISTENCY
|
||||
# CHECKS (not to mention doing the SendSQL() calls).
|
||||
#
|
||||
sub generateSqlDeltas {
|
||||
($#_ == 5) || confess("invalid number of arguments");
|
||||
my ( $self, # instance ptr to set representing the existing state
|
||||
$endState, # instance ptr to set representing the desired state
|
||||
$table, # table where these relations are kept
|
||||
$invariantName, # column held const for a RelationSet (often "bug_id")
|
||||
$invariantValue, # what to hold the above column constant at
|
||||
$columnName # the column which varies (often a userid)
|
||||
) = @_;
|
||||
|
||||
# construct the insert list by finding relations which exist in the
|
||||
# end state but not the current state.
|
||||
#
|
||||
my @endStateRelations = keys(%$endState);
|
||||
my @insertList = ();
|
||||
foreach ( @endStateRelations ) {
|
||||
push ( @insertList, $_ ) if ( ! exists $$self{"$_"} );
|
||||
}
|
||||
|
||||
# we've built the list. If it's non-null, add required sql chrome.
|
||||
#
|
||||
my $sqlInsert="";
|
||||
if ( $#insertList > -1 ) {
|
||||
$sqlInsert = "INSERT INTO $table ($invariantName, $columnName) VALUES " .
|
||||
join (",",
|
||||
map ( "($invariantValue, $_)" , @insertList )
|
||||
);
|
||||
}
|
||||
|
||||
# construct the delete list by seeing which relations exist in the
|
||||
# current state but not the end state
|
||||
#
|
||||
my @selfRelations = keys(%$self);
|
||||
my @deleteList = ();
|
||||
foreach ( @selfRelations ) {
|
||||
push (@deleteList, $_) if ( ! exists $$endState{"$_"} );
|
||||
}
|
||||
|
||||
# we've built the list. if it's non-empty, add required sql chrome.
|
||||
#
|
||||
my $sqlDelete = "";
|
||||
if ( $#deleteList > -1 ) {
|
||||
$sqlDelete = "DELETE FROM $table WHERE $invariantName = $invariantValue " .
|
||||
"AND $columnName IN ( " . join (",", @deleteList) . " )";
|
||||
}
|
||||
|
||||
return ($sqlInsert, $sqlDelete);
|
||||
}
|
||||
|
||||
# compare the current object with another.
|
||||
#
|
||||
sub isEqual {
|
||||
($#_ == 1) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
my $other = shift();
|
||||
|
||||
# get arrays of the keys for faster processing
|
||||
#
|
||||
my @selfRelations = keys(%$self);
|
||||
my @otherRelations = keys(%$other);
|
||||
|
||||
# make sure the arrays are the same size
|
||||
#
|
||||
return 0 if ( $#selfRelations != $#otherRelations );
|
||||
|
||||
# bail out if any of the elements are different
|
||||
#
|
||||
foreach my $relation ( @selfRelations ) {
|
||||
return 0 if ( !exists $$other{$relation})
|
||||
}
|
||||
|
||||
# we made it!
|
||||
#
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
# merge the results of a SQL command into this set
|
||||
#
|
||||
sub mergeFromDB {
|
||||
( $#_ == 1 ) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
|
||||
&::SendSQL(shift());
|
||||
while (my @row = &::FetchSQLData()) {
|
||||
$$self{$row[0]} = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# merge a set in string form into this set
|
||||
#
|
||||
sub mergeFromString {
|
||||
($#_ == 1) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
|
||||
# do the merge
|
||||
#
|
||||
foreach my $person (split(/[ ,]/, shift())) {
|
||||
if ($person ne "") {
|
||||
$$self{&::DBNameToIdAndCheck($person)} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# return the number of elements in this set
|
||||
#
|
||||
sub size {
|
||||
my $self = shift();
|
||||
|
||||
my @k = keys(%$self);
|
||||
return $#k++;
|
||||
}
|
||||
|
||||
# return this set in array form
|
||||
#
|
||||
sub toArray {
|
||||
my $self= shift();
|
||||
|
||||
return keys(%$self);
|
||||
}
|
||||
|
||||
# return this set in string form (comma-separated and sorted)
|
||||
#
|
||||
sub toString {
|
||||
($#_ == 0) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
|
||||
my @result = ();
|
||||
foreach my $i ( keys %$self ) {
|
||||
push @result, &::DBID_to_name($i);
|
||||
}
|
||||
|
||||
return join(',', sort(@result));
|
||||
}
|
||||
@@ -26,7 +26,7 @@ other necessary ingredient is a web server set up to run cgi scripts.
|
||||
|
||||
The software packages necessary for the proper running of bugzilla are:
|
||||
|
||||
1. MySQL database server and the mysql client
|
||||
1. MySQL database server and the mysql client (3.22.5 or greater)
|
||||
2. Perl (5.004 or greater)
|
||||
3. DBI Perl module
|
||||
4. Data::Dumper Perl module
|
||||
@@ -39,7 +39,7 @@ other necessary ingredient is a web server set up to run cgi scripts.
|
||||
Bugzilla has quite a few prerequisites, but none of them are TCL.
|
||||
Previous versions required TCL, but it no longer needed (or used).
|
||||
|
||||
1.1. Getting and setting up MySQL database
|
||||
1.1. Getting and setting up MySQL database (3.22.5 or greater)
|
||||
|
||||
Visit MySQL homepage at http://www.mysql.org and grab the latest stable
|
||||
release of the server. Both binaries and source are available and which
|
||||
|
||||
211
mozilla/webtools/bugzilla/RelationSet.pm
Normal file
211
mozilla/webtools/bugzilla/RelationSet.pm
Normal file
@@ -0,0 +1,211 @@
|
||||
#
|
||||
# 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) 2000 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dan Mosedale <dmose@mozilla.org>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
|
||||
# This object models a set of relations between one item and a group
|
||||
# of other items. An example is the set of relations between one bug
|
||||
# and the users CCed on that bug. Currently, the relation objects are
|
||||
# expected to be bugzilla userids. However, this could and perhaps
|
||||
# should be generalized to work with non userid objects, such as
|
||||
# keywords associated with a bug. That shouldn't be hard to do; it
|
||||
# might involve turning this into a virtual base class, and having
|
||||
# UserSet and KeywordSet types that inherit from it.
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
require "globals.pl";
|
||||
|
||||
package RelationSet;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
|
||||
# create a new empty RelationSet
|
||||
#
|
||||
sub new {
|
||||
my $type = shift();
|
||||
|
||||
# create a ref to an empty hash and bless it
|
||||
#
|
||||
my $self = {};
|
||||
bless $self, $type;
|
||||
|
||||
# construct from a comma-delimited string
|
||||
#
|
||||
if ($#_ == 0) {
|
||||
$self->mergeFromString($_[0]);
|
||||
}
|
||||
# unless this was a constructor for an empty list, somebody screwed up.
|
||||
#
|
||||
elsif ( $#_ != -1 ) {
|
||||
confess("invalid number of arguments");
|
||||
}
|
||||
|
||||
# bless as a RelationSet
|
||||
#
|
||||
return $self;
|
||||
}
|
||||
|
||||
# Assumes that the set of relations "FROM $table WHERE $constantSql and
|
||||
# $column = $value" is currently represented by $self, and this set should
|
||||
# be updated to look like $other.
|
||||
#
|
||||
# Returns an array of two strings, one INSERT and one DELETE, which will
|
||||
# make this change. Either or both strings may be the empty string,
|
||||
# meaning that no INSERT or DELETE or both (respectively) need to be done.
|
||||
#
|
||||
# THE CALLER IS RESPONSIBLE FOR ANY DESIRED LOCKING AND/OR CONSISTENCY
|
||||
# CHECKS (not to mention doing the SendSQL() calls).
|
||||
#
|
||||
sub generateSqlDeltas {
|
||||
($#_ == 5) || confess("invalid number of arguments");
|
||||
my ( $self, # instance ptr to set representing the existing state
|
||||
$endState, # instance ptr to set representing the desired state
|
||||
$table, # table where these relations are kept
|
||||
$invariantName, # column held const for a RelationSet (often "bug_id")
|
||||
$invariantValue, # what to hold the above column constant at
|
||||
$columnName # the column which varies (often a userid)
|
||||
) = @_;
|
||||
|
||||
# construct the insert list by finding relations which exist in the
|
||||
# end state but not the current state.
|
||||
#
|
||||
my @endStateRelations = keys(%$endState);
|
||||
my @insertList = ();
|
||||
foreach ( @endStateRelations ) {
|
||||
push ( @insertList, $_ ) if ( ! exists $$self{"$_"} );
|
||||
}
|
||||
|
||||
# we've built the list. If it's non-null, add required sql chrome.
|
||||
#
|
||||
my $sqlInsert="";
|
||||
if ( $#insertList > -1 ) {
|
||||
$sqlInsert = "INSERT INTO $table ($invariantName, $columnName) VALUES " .
|
||||
join (",",
|
||||
map ( "($invariantValue, $_)" , @insertList )
|
||||
);
|
||||
}
|
||||
|
||||
# construct the delete list by seeing which relations exist in the
|
||||
# current state but not the end state
|
||||
#
|
||||
my @selfRelations = keys(%$self);
|
||||
my @deleteList = ();
|
||||
foreach ( @selfRelations ) {
|
||||
push (@deleteList, $_) if ( ! exists $$endState{"$_"} );
|
||||
}
|
||||
|
||||
# we've built the list. if it's non-empty, add required sql chrome.
|
||||
#
|
||||
my $sqlDelete = "";
|
||||
if ( $#deleteList > -1 ) {
|
||||
$sqlDelete = "DELETE FROM $table WHERE $invariantName = $invariantValue " .
|
||||
"AND $columnName IN ( " . join (",", @deleteList) . " )";
|
||||
}
|
||||
|
||||
return ($sqlInsert, $sqlDelete);
|
||||
}
|
||||
|
||||
# compare the current object with another.
|
||||
#
|
||||
sub isEqual {
|
||||
($#_ == 1) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
my $other = shift();
|
||||
|
||||
# get arrays of the keys for faster processing
|
||||
#
|
||||
my @selfRelations = keys(%$self);
|
||||
my @otherRelations = keys(%$other);
|
||||
|
||||
# make sure the arrays are the same size
|
||||
#
|
||||
return 0 if ( $#selfRelations != $#otherRelations );
|
||||
|
||||
# bail out if any of the elements are different
|
||||
#
|
||||
foreach my $relation ( @selfRelations ) {
|
||||
return 0 if ( !exists $$other{$relation})
|
||||
}
|
||||
|
||||
# we made it!
|
||||
#
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
# merge the results of a SQL command into this set
|
||||
#
|
||||
sub mergeFromDB {
|
||||
( $#_ == 1 ) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
|
||||
&::SendSQL(shift());
|
||||
while (my @row = &::FetchSQLData()) {
|
||||
$$self{$row[0]} = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# merge a set in string form into this set
|
||||
#
|
||||
sub mergeFromString {
|
||||
($#_ == 1) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
|
||||
# do the merge
|
||||
#
|
||||
foreach my $person (split(/[ ,]/, shift())) {
|
||||
if ($person ne "") {
|
||||
$$self{&::DBNameToIdAndCheck($person)} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# return the number of elements in this set
|
||||
#
|
||||
sub size {
|
||||
my $self = shift();
|
||||
|
||||
my @k = keys(%$self);
|
||||
return $#k++;
|
||||
}
|
||||
|
||||
# return this set in array form
|
||||
#
|
||||
sub toArray {
|
||||
my $self= shift();
|
||||
|
||||
return keys(%$self);
|
||||
}
|
||||
|
||||
# return this set in string form (comma-separated and sorted)
|
||||
#
|
||||
sub toString {
|
||||
($#_ == 0) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
|
||||
my @result = ();
|
||||
foreach my $i ( keys %$self ) {
|
||||
push @result, &::DBID_to_name($i);
|
||||
}
|
||||
|
||||
return join(',', sort(@result));
|
||||
}
|
||||
@@ -22,6 +22,8 @@
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
use RelationSet;
|
||||
|
||||
# Shut up misguided -w warnings about "used only once". For some reason,
|
||||
# "use vars" chokes on me when I try it here.
|
||||
|
||||
@@ -147,8 +149,10 @@ my $sev_popup = make_options(\@::legal_severity, $bug{'bug_severity'});
|
||||
my $component_popup = make_options($::components{$bug{'product'}},
|
||||
$bug{'component'});
|
||||
|
||||
my $ccSet = new RelationSet;
|
||||
$ccSet->mergeFromDB("select who from cc where bug_id=$id");
|
||||
my $cc_element = '<INPUT NAME=cc SIZE=30 VALUE="' .
|
||||
ShowCcList($id) . '">';
|
||||
$ccSet->toString() . '">';
|
||||
|
||||
|
||||
my $URL = $bug{'bug_file_loc'};
|
||||
@@ -208,7 +212,9 @@ if (Param("usetargetmilestone")) {
|
||||
if ($url eq "") {
|
||||
$url = "notargetmilestone.html";
|
||||
}
|
||||
|
||||
if ($bug{'target_milestone'} eq "") {
|
||||
$bug{'target_milestone'} = " ";
|
||||
}
|
||||
print "
|
||||
<TD ALIGN=RIGHT><A href=\"$url\"><B>Target Milestone:</B></A></TD>
|
||||
<TD><SELECT NAME=target_milestone>" .
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#
|
||||
# Contributor(s): Holger Schurig <holgerschurig@nikocity.de>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
#
|
||||
#
|
||||
# Direct any questions on this source code to
|
||||
@@ -591,8 +592,15 @@ $table{cc} =
|
||||
'bug_id mediumint not null,
|
||||
who mediumint not null,
|
||||
|
||||
index(bug_id),
|
||||
index(who)';
|
||||
index(who),
|
||||
unique(bug_id,who)';
|
||||
|
||||
$table{watch} =
|
||||
'watcher mediumint not null,
|
||||
watched mediumint not null,
|
||||
|
||||
index(watched),
|
||||
unique(watcher,watched)';
|
||||
|
||||
|
||||
$table{longdescs} =
|
||||
@@ -742,8 +750,8 @@ $table{keywords} =
|
||||
'bug_id mediumint not null,
|
||||
keywordid smallint not null,
|
||||
|
||||
index(bug_id),
|
||||
index(keywordid)';
|
||||
index(keywordid),
|
||||
unique(bug_id,keywordid)';
|
||||
|
||||
$table{keyworddefs} =
|
||||
'id smallint not null primary key,
|
||||
@@ -994,6 +1002,49 @@ sub GetIndexDef ($$)
|
||||
}
|
||||
}
|
||||
|
||||
sub CountIndexes ($)
|
||||
{
|
||||
my ($table) = @_;
|
||||
|
||||
my $sth = $dbh->prepare("SHOW INDEX FROM $table");
|
||||
$sth->execute;
|
||||
|
||||
if ( $sth->rows == -1 ) {
|
||||
die ("Unexpected response while counting indexes in $table:" .
|
||||
" \$sth->rows == -1");
|
||||
}
|
||||
|
||||
return ($sth->rows);
|
||||
}
|
||||
|
||||
sub DropIndexes ($)
|
||||
{
|
||||
my ($table) = @_;
|
||||
my %SEEN;
|
||||
|
||||
# get the list of indexes
|
||||
#
|
||||
my $sth = $dbh->prepare("SHOW INDEX FROM $table");
|
||||
$sth->execute;
|
||||
|
||||
# drop each index
|
||||
#
|
||||
while ( my $ref = $sth->fetchrow_arrayref) {
|
||||
|
||||
# note that some indexes are described by multiple rows in the
|
||||
# index table, so we may have already dropped the index described
|
||||
# in the current row.
|
||||
#
|
||||
next if exists $SEEN{$$ref[2]};
|
||||
|
||||
my $dropSth = $dbh->prepare("ALTER TABLE $table DROP INDEX $$ref[2]");
|
||||
$dropSth->execute;
|
||||
$dropSth->finish;
|
||||
$SEEN{$$ref[2]} = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#
|
||||
# Check if the enums in the bugs table return the same values that are defined
|
||||
# in the various locally changeable variables. If this is true, then alter the
|
||||
@@ -1506,7 +1557,6 @@ AddField('products', 'maxvotesperbug', 'smallint not null default 10000');
|
||||
AddField('products', 'votestoconfirm', 'smallint not null');
|
||||
AddField('profiles', 'blessgroupset', 'bigint not null');
|
||||
|
||||
|
||||
# 2000-03-21 Adding a table for target milestones to
|
||||
# database - matthew@zeroknowledge.com
|
||||
|
||||
@@ -1577,6 +1627,29 @@ if (!GetFieldDef('products', 'defaultmilestone')) {
|
||||
}
|
||||
}
|
||||
|
||||
# 2000-03-24 Added unique indexes into the cc and keyword tables. This
|
||||
# prevents certain database inconsistencies, and, moreover, is required for
|
||||
# new generalized list code to work.
|
||||
|
||||
if ( CountIndexes('cc') != 3 ) {
|
||||
|
||||
# XXX should eliminate duplicate entries before altering
|
||||
#
|
||||
print "Recreating indexes on cc table.\n";
|
||||
DropIndexes('cc');
|
||||
$dbh->do("ALTER TABLE cc ADD UNIQUE (bug_id,who)");
|
||||
$dbh->do("ALTER TABLE cc ADD INDEX (who)");
|
||||
}
|
||||
|
||||
if ( CountIndexes('keywords') != 3 ) {
|
||||
|
||||
# XXX should eliminate duplicate entries before altering
|
||||
#
|
||||
print "Recreating indexes on keywords table.\n";
|
||||
DropIndexes('keywords');
|
||||
$dbh->do("ALTER TABLE keywords ADD INDEX (keywordid)");
|
||||
$dbh->do("ALTER TABLE keywords ADD UNIQUE (bug_id,keywordid)");
|
||||
}
|
||||
|
||||
#
|
||||
# If you had to change the --TABLE-- definition in any way, then add your
|
||||
@@ -1594,4 +1667,6 @@ if ($regenerateshadow) {
|
||||
print "Now regenerating the shadow database for all bugs.\n";
|
||||
system("./processmail regenerate");
|
||||
}
|
||||
|
||||
unlink "data/versioncache";
|
||||
print "Reminder: Bugzilla now requires version 3.22.5 or later of MySQL.\n";
|
||||
|
||||
@@ -550,6 +550,10 @@ DefParam("commentonclose",
|
||||
DefParam("commentonduplicate",
|
||||
"If this option is on, the user needs to enter a short comment if the bug is marked as duplicate",
|
||||
"b", 0 );
|
||||
|
||||
DefParam("supportwatchers",
|
||||
"Support one user watching (ie getting copies of all related email" .
|
||||
" about) another's bugs. Useful for people going on vacation, and" .
|
||||
" QA folks watching particular developers' bugs",
|
||||
"b", 0 );
|
||||
1;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
|
||||
# Contains some global variables and routines used throughout bugzilla.
|
||||
|
||||
@@ -65,6 +66,7 @@ use Mysql;
|
||||
use Date::Format; # For time2str().
|
||||
use Date::Parse; # For str2time().
|
||||
# use Carp; # for confess
|
||||
use RelationSet;
|
||||
|
||||
# Contains the version string for the current running Bugzilla.
|
||||
$::param{'version'} = '2.9';
|
||||
@@ -118,9 +120,6 @@ sub SqlLog {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
sub SendSQL {
|
||||
my ($str, $dontshadow) = (@_);
|
||||
my $iswrite = ($str =~ /^(INSERT|REPLACE|UPDATE|DELETE)/i);
|
||||
@@ -756,23 +755,13 @@ sub GetLongDescriptionAsHTML {
|
||||
}
|
||||
|
||||
sub ShowCcList {
|
||||
my ($num) = (@_);
|
||||
my @ccids;
|
||||
my @row;
|
||||
SendSQL("select who from cc where bug_id = $num");
|
||||
while (@row = FetchSQLData()) {
|
||||
push(@ccids, $row[0]);
|
||||
}
|
||||
my @result = ();
|
||||
foreach my $i (@ccids) {
|
||||
push @result, DBID_to_name($i);
|
||||
}
|
||||
|
||||
return join(',', @result);
|
||||
my ($num) = (@_);
|
||||
|
||||
my $ccSet = new RelationSet();
|
||||
$ccSet->mergeFromDB("select who from cc where bug_id=$num");
|
||||
return $ccSet->toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Fills in a hashtable with info about the columns for the given table in the
|
||||
# database. The hashtable has the following entries:
|
||||
# -list- the list of column names
|
||||
@@ -903,7 +892,7 @@ sub RemoveVotes {
|
||||
}
|
||||
|
||||
|
||||
sub Param {
|
||||
sub Param ($) {
|
||||
my ($value) = (@_);
|
||||
if (defined $::param{$value}) {
|
||||
return $::param{$value};
|
||||
|
||||
@@ -28,6 +28,7 @@ my $UserInEditGroupSet = -1;
|
||||
my $UserInCanConfirmGroupSet = -1;
|
||||
|
||||
require "CGI.pl";
|
||||
use RelationSet;
|
||||
|
||||
# Shut up misguided -w warnings about "used only once":
|
||||
|
||||
@@ -373,24 +374,21 @@ if (defined $::FORM{'qa_contact'}) {
|
||||
|
||||
ConnectToDatabase();
|
||||
|
||||
my %ccids;
|
||||
my $origcclist = "";
|
||||
my $formCcSet = new RelationSet;
|
||||
my $origCcSet = new RelationSet;
|
||||
my $origCcString;
|
||||
|
||||
# We make sure to check out the CC list before we actually start touching any
|
||||
# bugs.
|
||||
# bugs. mergeFromString() ultimately searches the database using a quoted
|
||||
# form of the data it gets from $::FORM{'cc'}, so anything bogus from a
|
||||
# security standpoint should trigger an abort there.
|
||||
#
|
||||
if (defined $::FORM{'cc'} && defined $::FORM{'id'}) {
|
||||
$origcclist = ShowCcList($::FORM{'id'});
|
||||
if ($origcclist ne $::FORM{'cc'}) {
|
||||
foreach my $person (split(/[ ,]/, $::FORM{'cc'})) {
|
||||
if ($person ne "") {
|
||||
my $cid = DBNameToIdAndCheck($person);
|
||||
$ccids{$cid} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
$origCcSet->mergeFromDB("select who from cc where bug_id = $::FORM{'id'}");
|
||||
$origCcString = $origCcSet->toString(); # cache a copy of the string vers
|
||||
$formCcSet->mergeFromString($::FORM{'cc'});
|
||||
}
|
||||
|
||||
|
||||
if ( Param('strictvaluechecks') ) {
|
||||
CheckFormFieldDefined(\%::FORM, 'knob');
|
||||
}
|
||||
@@ -759,22 +757,25 @@ The changes made were:
|
||||
AppendComment($id, $::FORM{'who'}, $::FORM{'comment'});
|
||||
}
|
||||
|
||||
if (defined $::FORM{'cc'} && $origcclist ne $::FORM{'cc'}) {
|
||||
SendSQL("delete from cc where bug_id = $id");
|
||||
foreach my $ccid (keys %ccids) {
|
||||
SendSQL("insert into cc (bug_id, who) values ($id, $ccid)");
|
||||
}
|
||||
my $newcclist = ShowCcList($id);
|
||||
if ($newcclist ne $origcclist) {
|
||||
my $col = GetFieldID('cc');
|
||||
my $origq = SqlQuote($origcclist);
|
||||
my $newq = SqlQuote($newcclist);
|
||||
SendSQL("INSERT INTO bugs_activity " .
|
||||
"(bug_id,who,bug_when,fieldid,oldvalue,newvalue) VALUES " .
|
||||
"($id,$whoid,'$timestamp',$col,$origq,$newq)");
|
||||
}
|
||||
}
|
||||
if (defined $::FORM{'cc'} && defined $::FORM{'id'}
|
||||
&& ! $origCcSet->isEqual($formCcSet) ) {
|
||||
|
||||
# update the database to look like the form
|
||||
#
|
||||
my @CCDELTAS = $origCcSet->generateSqlDeltas($formCcSet, "cc",
|
||||
"bug_id", $::FORM{'id'},
|
||||
"who");
|
||||
$CCDELTAS[0] eq "" || SendSQL($CCDELTAS[0]);
|
||||
$CCDELTAS[1] eq "" || SendSQL($CCDELTAS[1]);
|
||||
|
||||
my $col = GetFieldID('cc');
|
||||
my $origq = SqlQuote($origCcString);
|
||||
my $newq = SqlQuote($::FORM{'cc'});
|
||||
SendSQL("INSERT INTO bugs_activity " .
|
||||
"(bug_id,who,bug_when,fieldid,oldvalue,newvalue) VALUES " .
|
||||
"($id,$whoid,'$timestamp',$col,$origq,$newq)");
|
||||
}
|
||||
|
||||
|
||||
if (defined $::FORM{'dependson'}) {
|
||||
my $me = "blocked";
|
||||
@@ -850,9 +851,9 @@ The changes made were:
|
||||
if ($col eq 'assigned_to' || $col eq 'qa_contact') {
|
||||
$old = DBID_to_name($old) if $old != 0;
|
||||
$new = DBID_to_name($new) if $new != 0;
|
||||
$origcclist .= ",$old"; # make sure to send mail to people
|
||||
# if they are going to no longer get
|
||||
# updates about this bug.
|
||||
$origCcString .= ",$old"; # make sure to send mail to people
|
||||
# if they are going to no longer get
|
||||
# updates about this bug.
|
||||
}
|
||||
if ($col eq 'product') {
|
||||
RemoveVotes($id, 0,
|
||||
@@ -869,7 +870,7 @@ The changes made were:
|
||||
|
||||
print "<TABLE BORDER=1><TD><H2>Changes to bug $id submitted</H2>\n";
|
||||
SendSQL("unlock tables");
|
||||
system("./processmail", "-forcecc", $origcclist, $id, $::FORM{'who'});
|
||||
system("./processmail", "-forcecc", $origCcString, $id, $::FORM{'who'});
|
||||
print "<TD><A HREF=\"show_bug.cgi?id=$id\">Back To BUG# $id</A></TABLE>\n";
|
||||
|
||||
foreach my $k (keys(%dependencychanged)) {
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>,
|
||||
# Bryce Nesbitt <bryce-mozilla@nextbus.com>
|
||||
# Bryce Nesbitt <bryce-mozilla@nextbus.com>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
|
||||
# To recreate the shadow database, run "processmail regenerate" .
|
||||
|
||||
@@ -28,6 +29,8 @@ use strict;
|
||||
|
||||
require "globals.pl";
|
||||
|
||||
use RelationSet;
|
||||
|
||||
$| = 1;
|
||||
|
||||
umask(0);
|
||||
@@ -100,11 +103,11 @@ sub Different {
|
||||
|
||||
|
||||
sub DescCC {
|
||||
my ($cclist) = (@_);
|
||||
if (scalar(@$cclist) <= 0) {
|
||||
return "";
|
||||
}
|
||||
return "Cc: " . join(", ", @$cclist) . "\n";
|
||||
my $cclist = shift();
|
||||
|
||||
return "" if ( $cclist->size() == 0 );
|
||||
|
||||
return "Cc: " . $cclist->toString() . "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -201,20 +204,20 @@ sub GetBugText {
|
||||
|
||||
$::bug{'long_desc'} = GetLongDescriptionAsText($id);
|
||||
|
||||
my @cclist;
|
||||
@cclist = split(/,/, ShowCcList($id));
|
||||
my $cclist = new RelationSet();
|
||||
$cclist->mergeFromDB("select who from cc where bug_id = $id");
|
||||
my @voterlist;
|
||||
SendSQL("select profiles.login_name from votes, profiles where votes.bug_id = $id and profiles.userid = votes.who");
|
||||
while (MoreSQLData()) {
|
||||
my $v = FetchOneColumn();
|
||||
push(@voterlist, $v);
|
||||
}
|
||||
$::bug{'cclist'} = join(',', @cclist);
|
||||
$::bug{'cclist'} = $cclist->toString();
|
||||
$::bug{'voterlist'} = join(',', @voterlist);
|
||||
|
||||
if (Param("prettyasciimail")) {
|
||||
$^A = "";
|
||||
my $temp = formline <<'END',$::bug{'short_desc'},$id,$::bug{'product'},$::bug{'bug_status'},$::bug{'version'},$::bug{'resolution'},$::bug{'rep_platform'},$::bug{'bug_severity'},$::bug{'op_sys'},$::bug{'priority'},$::bug{'component'},$::bug{'assigned_to'},$::bug{'reporter'},$qa_contact,DescCC(\@cclist),$target_milestone,${status_whiteboard},$::bug{'bug_file_loc'},DescDependencies($id);
|
||||
my $temp = formline <<'END',$::bug{'short_desc'},$id,$::bug{'product'},$::bug{'bug_status'},$::bug{'version'},$::bug{'resolution'},$::bug{'rep_platform'},$::bug{'bug_severity'},$::bug{'op_sys'},$::bug{'priority'},$::bug{'component'},$::bug{'assigned_to'},$::bug{'reporter'},$qa_contact,DescCC($cclist),$target_milestone,${status_whiteboard},$::bug{'bug_file_loc'},DescDependencies($id);
|
||||
+============================================================================+
|
||||
| @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|
||||
+----------------------------------------------------------------------------+
|
||||
@@ -255,7 +258,7 @@ Component: $::bug{'component'}
|
||||
AssignedTo: $::bug{'assigned_to'}
|
||||
ReportedBy: $::bug{'reporter'}
|
||||
$qa_contact$target_milestone${status_whiteboard}URL: $::bug{'bug_file_loc'}
|
||||
" . DescCC(\@cclist) . "Summary: $::bug{'short_desc'}
|
||||
" . DescCC($cclist) . "Summary: $::bug{'short_desc'}
|
||||
" . DescDependencies($id) . "
|
||||
$::bug{'long_desc'}
|
||||
";
|
||||
@@ -357,7 +360,9 @@ sub NewProcessOneBug {
|
||||
$values{$i} = shift(@row);
|
||||
}
|
||||
my ($start, $end) = (@row);
|
||||
$values{'cc'} = ShowCcList($id);
|
||||
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 " .
|
||||
@@ -463,137 +468,140 @@ sub NewProcessOneBug {
|
||||
@voterlist,
|
||||
@forcecc) {
|
||||
$count++;
|
||||
if ($seen{$person}) {
|
||||
next;
|
||||
}
|
||||
|
||||
SendSQL("SELECT userid, emailnotification, newemailtech," .
|
||||
" groupset & $values{'groupset'} " .
|
||||
"FROM profiles WHERE login_name = " . SqlQuote($person));
|
||||
my ($userid, $emailnotification, $newemailtech,
|
||||
$groupset) = (FetchSQLData());
|
||||
if (!$newemailtech || !Param('newemailtech')) {
|
||||
next;
|
||||
}
|
||||
$seen{$person} = 1;
|
||||
if ($groupset ne $values{'groupset'}) {
|
||||
next;
|
||||
}
|
||||
if ($emailnotification eq "ExcludeSelfChanges" &&
|
||||
lc($person) eq $nametoexclude) {
|
||||
$didexclude = 1;
|
||||
next;
|
||||
}
|
||||
if ($emailnotification eq "CCOnly" && $count < 3) {
|
||||
next;
|
||||
}
|
||||
|
||||
my %mailhead = %defmailhead;
|
||||
|
||||
# SendSQL("SELECT name, mailhead, maildiffs FROM diffprefs, fielddefs WHERE fielddefs.fieldid = diffprefs.fieldid AND userid = $userid");
|
||||
# while (MoreSQLData()) {
|
||||
# my ($field, $h, $d) = (FetchSQLData());
|
||||
# $mailhead{$field} = $h;
|
||||
# $maildiffs{$field} = $d;
|
||||
# }
|
||||
|
||||
# my $maxlen = 0;
|
||||
# foreach my $f (keys %mailhead) {
|
||||
# if ($mailhead{$f}) {
|
||||
# my $l = length($fielddescription{$f});
|
||||
# if ($maxlen < $l) {
|
||||
# $maxlen = $l;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
my $head = "";
|
||||
|
||||
foreach my $f (@headerlist) {
|
||||
if ($mailhead{$f}) {
|
||||
my $value = $values{$f};
|
||||
if (!defined $value) {
|
||||
# Probaby ought to whine or something. ###
|
||||
next;
|
||||
}
|
||||
my $desc = $fielddescription{$f};
|
||||
$head .= FormatDouble($desc, $value);
|
||||
|
||||
# my $extra = $maxlen - length($desc);
|
||||
# $head .= ($extra x " ");
|
||||
# $head .= $desc . ": ";
|
||||
# while (1) {
|
||||
# if (length($value) < 70) {
|
||||
# $head .= $value . "\n";
|
||||
# last;
|
||||
# }
|
||||
# my $pos = rindex($value, " ", 70);
|
||||
# if ($pos < 0) {
|
||||
# $pos = rindex($value, ",", 70);
|
||||
# if ($pos < 0) {
|
||||
# $pos = 70;
|
||||
# }
|
||||
# }
|
||||
# $head .= substr($value, 0, 70) . "\n";
|
||||
# $head .= (($extra + 2) x " ");
|
||||
# $value = substr($value, 70);
|
||||
# }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($difftext eq "" && $newcomments eq "") {
|
||||
# Whoops, no differences!
|
||||
next;
|
||||
}
|
||||
|
||||
my $isnew = ($start !~ m/[1-9]/);
|
||||
|
||||
my %substs;
|
||||
$substs{"neworchanged"} = $isnew ? "New" : "Changed";
|
||||
$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'};
|
||||
|
||||
my $template = Param("newchangedmail");
|
||||
|
||||
my $msg = PerformSubsts($template, \%substs);
|
||||
open(SENDMAIL, "|/usr/lib/sendmail -t") ||
|
||||
die "Can't open sendmail";
|
||||
|
||||
print SENDMAIL trim($msg) . "\n";
|
||||
close SENDMAIL;
|
||||
push(@sentlist, $person);
|
||||
NewProcessOnePerson($person, $count, \@headerlist, \%values,
|
||||
\%defmailhead, \%fielddescription, $difftext,
|
||||
$newcomments, $start, $id, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SendSQL("UPDATE bugs SET lastdiffed = '$end', delta_ts = delta_ts " .
|
||||
"WHERE bug_id = $id");
|
||||
}
|
||||
|
||||
sub NewProcessOnePerson ($$\@\%\%\%$$$$) {
|
||||
my ($person, $count, $hlRef, $valueRef, $dmhRef, $fdRef, $difftext,
|
||||
$newcomments, $start, $id, $checkWatchers) = @_;
|
||||
|
||||
my %values = %$valueRef;
|
||||
my @headerlist = @$hlRef;
|
||||
my %defmailhead = %$dmhRef;
|
||||
my %fielddescription = %$fdRef;
|
||||
|
||||
if ($seen{$person}) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendSQL("SELECT userid, emailnotification, newemailtech," .
|
||||
" groupset & $values{'groupset'} " .
|
||||
"FROM profiles WHERE login_name = " . SqlQuote($person));
|
||||
my ($userid, $emailnotification, $newemailtech,
|
||||
$groupset) = (FetchSQLData());
|
||||
|
||||
# check for watchers, and recurse if we find any, but tell the
|
||||
# recursive call not to check for watchers
|
||||
#
|
||||
if (Param("supportwatchers") && $checkWatchers) {
|
||||
my $personId = DBname_to_id($person);
|
||||
my $watcherSet = new RelationSet();
|
||||
$watcherSet->mergeFromDB("SELECT watcher FROM watch WHERE" .
|
||||
" watched = $personId");
|
||||
|
||||
foreach my $watcher ( $watcherSet->toArray() ) {
|
||||
|
||||
NewProcessOnePerson(DBID_to_name($watcher),
|
||||
$count, \@headerlist, \%values,
|
||||
\%defmailhead, \%fielddescription, $difftext,
|
||||
$newcomments, $start, $id, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!$newemailtech || !Param('newemailtech')) {
|
||||
return;
|
||||
}
|
||||
$seen{$person} = 1;
|
||||
|
||||
|
||||
# if this person doesn't have permission to see info on this bug,
|
||||
# return.
|
||||
#
|
||||
# XXX - I _think_ 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.
|
||||
#
|
||||
if ($groupset ne $values{'groupset'}) {
|
||||
return;
|
||||
}
|
||||
if ($emailnotification eq "ExcludeSelfChanges" &&
|
||||
lc($person) eq $nametoexclude) {
|
||||
$didexclude = 1;
|
||||
return;
|
||||
}
|
||||
# "$count < 3" means "this person is either assigned_to or reporter"
|
||||
#
|
||||
if ($emailnotification eq "CCOnly" && $count < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
my %mailhead = %defmailhead;
|
||||
|
||||
my $head = "";
|
||||
|
||||
foreach my $f (@headerlist) {
|
||||
if ($mailhead{$f}) {
|
||||
my $value = $values{$f};
|
||||
if (!defined $value) {
|
||||
# Probaby ought to whine or something. ###
|
||||
next;
|
||||
}
|
||||
my $desc = $fielddescription{$f};
|
||||
$head .= FormatDouble($desc, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($difftext eq "" && $newcomments eq "") {
|
||||
# Whoops, no differences!
|
||||
return;
|
||||
}
|
||||
|
||||
my $isnew = ($start !~ m/[1-9]/);
|
||||
|
||||
my %substs;
|
||||
$substs{"neworchanged"} = $isnew ? "New" : "Changed";
|
||||
$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'};
|
||||
|
||||
my $template = Param("newchangedmail");
|
||||
|
||||
my $msg = PerformSubsts($template, \%substs);
|
||||
open(SENDMAIL, "|/usr/lib/sendmail -t") ||
|
||||
die "Can't open sendmail";
|
||||
|
||||
print SENDMAIL trim($msg) . "\n";
|
||||
close SENDMAIL;
|
||||
push(@sentlist, $person);
|
||||
|
||||
}
|
||||
|
||||
|
||||
sub ProcessOneBug {
|
||||
my $i = $_[0];
|
||||
NewProcessOneBug($i);
|
||||
my $old = "shadow/$i";
|
||||
my $new = "shadow/$i.tmp.$$";
|
||||
my $diffs = "shadow/$i.diffs.$$";
|
||||
my $verb = "Changed";
|
||||
if (!stat($old)) {
|
||||
mkdir "shadow", 0777;
|
||||
chmod 0777, "shadow";
|
||||
my $i = $_[0];
|
||||
NewProcessOneBug($i);
|
||||
my $old = "shadow/$i";
|
||||
my $new = "shadow/$i.tmp.$$";
|
||||
my $diffs = "shadow/$i.diffs.$$";
|
||||
my $verb = "Changed";
|
||||
if (!stat($old)) {
|
||||
mkdir "shadow", 0777;
|
||||
chmod 0777, "shadow";
|
||||
open(OLD, ">$old") || die "Couldn't create null $old";
|
||||
close OLD;
|
||||
$verb = "New";
|
||||
@@ -683,7 +691,7 @@ if (open(FID, "<data/nomail")) {
|
||||
}
|
||||
|
||||
# To recreate the shadow database, run "processmail regenerate" .
|
||||
if ($ARGV[0] eq "regenerate") {
|
||||
if ($#ARGV >= 0 && $ARGV[0] eq "regenerate") {
|
||||
$regenerate = 1;
|
||||
shift @ARGV;
|
||||
SendSQL("select bug_id from bugs order by bug_id");
|
||||
@@ -700,7 +708,7 @@ if ($ARGV[0] eq "regenerate") {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($ARGV[0] eq "-forcecc") {
|
||||
if ($#ARGV >= 0 && $ARGV[0] eq "-forcecc") {
|
||||
shift(@ARGV);
|
||||
foreach my $i (split(/,/, shift(@ARGV))) {
|
||||
push(@forcecc, trim($i));
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
require "CGI.pl";
|
||||
|
||||
use RelationSet;
|
||||
|
||||
# Shut up misguided -w warnings about "used only once". "use vars" just
|
||||
# doesn't work for me.
|
||||
sub sillyness {
|
||||
@@ -134,6 +137,29 @@ risk any bugs), check here.
|
||||
EmitEntry("Check here to sign up (and risk any bugs)",
|
||||
qq{<INPUT TYPE="checkbox" NAME="newemailtech" $checkedpart>New email tech});
|
||||
}
|
||||
|
||||
if (Param("supportwatchers")) {
|
||||
my $watcheduserSet = new RelationSet;
|
||||
$watcheduserSet->mergeFromDB("SELECT watched FROM watch WHERE" .
|
||||
" watcher=$userid");
|
||||
my $watchedusers = $watcheduserSet->toString();
|
||||
|
||||
print qq{
|
||||
<TR><TD COLSPAN="2"><HR></TD></TR>
|
||||
<TR><TD COLSPAN="2"><FONT COLOR="red">New!</FONT>
|
||||
If you want to help cover for someone when they're on vacation, or if
|
||||
you need to do the QA related to all of their bugs, you can tell bugzilla
|
||||
to send mail related to their bugs to you also. List the email addresses
|
||||
of any users you wish to watch here, separated by commas.
|
||||
<FONT COLOR="red">Note that you MUST have the above "New email tech"
|
||||
button selected in order to use this feature.</FONT>
|
||||
</TD></TR>
|
||||
};
|
||||
EmitEntry("Users to watch",
|
||||
qq{<INPUT SIZE=35 NAME="watchedusers" VALUE="$watchedusers">});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub SaveDiffs {
|
||||
@@ -144,6 +170,47 @@ sub SaveDiffs {
|
||||
SendSQL("UPDATE profiles " .
|
||||
"SET emailnotification = " . SqlQuote($::FORM{'emailnotification'})
|
||||
. ", newemailtech = $newemailtech WHERE userid = $userid");
|
||||
|
||||
# deal with any watchers
|
||||
#
|
||||
if (Param("supportwatchers") ) {
|
||||
|
||||
if (exists $::FORM{'watchedusers'}) {
|
||||
|
||||
Error ('You must have "New email tech" set to watch someone')
|
||||
if ( $::FORM{'watchedusers'} ne "" && $newemailtech == 0);
|
||||
|
||||
# Just in case. Note that this much locking is actually overkill:
|
||||
# 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");
|
||||
|
||||
# what the db looks like now
|
||||
#
|
||||
my $origWatchedUsers = new RelationSet;
|
||||
$origWatchedUsers->mergeFromDB("SELECT watched FROM watch WHERE" .
|
||||
" watcher=$userid");
|
||||
|
||||
# update the database to look like the form
|
||||
#
|
||||
my $newWatchedUsers = new RelationSet($::FORM{'watchedusers'});
|
||||
my @CCDELTAS = $origWatchedUsers->generateSqlDeltas(
|
||||
$newWatchedUsers,
|
||||
"watch",
|
||||
"watcher",
|
||||
$userid,
|
||||
"watched");
|
||||
$CCDELTAS[0] eq "" || SendSQL($CCDELTAS[0]);
|
||||
$CCDELTAS[1] eq "" || SendSQL($CCDELTAS[1]);
|
||||
|
||||
# all done
|
||||
#
|
||||
SendSQL("UNLOCK TABLES");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user