From 82ed1771ce6fe3608cbc5ed039bd795bbe21a8e4 Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" Date: Wed, 3 Jul 2002 23:07:14 +0000 Subject: [PATCH] Fix for bug 99203: Implements bug aliases feature. r=bbaetz,jouni --- webtools/bugzilla/Bug.pm | 13 ++- webtools/bugzilla/Bugzilla/Bug.pm | 13 ++- webtools/bugzilla/CGI.pl | 55 +++++++++-- webtools/bugzilla/bug_form.pl | 4 +- webtools/bugzilla/bugzilla.dtd | 3 +- webtools/bugzilla/checksetup.pl | 48 ++++++++-- webtools/bugzilla/defparams.pl | 6 ++ webtools/bugzilla/process_bug.cgi | 94 +++++++++++++++---- .../template/en/default/bug/edit.html.tmpl | 6 ++ 9 files changed, 194 insertions(+), 48 deletions(-) diff --git a/webtools/bugzilla/Bug.pm b/webtools/bugzilla/Bug.pm index 7ac2bd1012c..7e7478c4674 100755 --- a/webtools/bugzilla/Bug.pm +++ b/webtools/bugzilla/Bug.pm @@ -33,7 +33,7 @@ package Bug; use CGI::Carp qw(fatalsToBrowser); my %ok_field; -for my $key (qw (bug_id product version rep_platform op_sys bug_status +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 @@ -74,9 +74,12 @@ sub initBug { my $self = shift(); my ($bug_id, $user_id) = (@_); + # If the bug ID isn't numeric, it might be an alias, so try to convert it. + $bug_id = &::BugAliasToID($bug_id) if $bug_id !~ /^[1-9][0-9]*$/; + my $old_bug_id = $bug_id; if ((! defined $bug_id) || (!$bug_id) || (!&::detaint_natural($bug_id))) { - # no bug number given + # no bug number given or the alias didn't match a bug $self->{'bug_id'} = $old_bug_id; $self->{'error'} = "InvalidBugId"; return $self; @@ -108,7 +111,7 @@ sub initBug { my $query = " select - bugs.bug_id, product, version, rep_platform, op_sys, bug_status, + bugs.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, date_format(creation_ts,'%Y-%m-%d %H:%i'), @@ -123,7 +126,7 @@ sub initBug { if (@row = &::FetchSQLData()) { my $count = 0; my %fields; - foreach my $field ("bug_id", "product", "version", "rep_platform", + 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", @@ -248,7 +251,7 @@ sub emitXML { $xml .= "\n"; - foreach my $field ("bug_id", "bug_status", "product", + foreach my $field ("bug_id", "alias", "bug_status", "product", "priority", "version", "rep_platform", "assigned_to", "delta_ts", "component", "reporter", "target_milestone", "bug_severity", "creation_ts", "qa_contact", "op_sys", "resolution", "bug_file_loc", diff --git a/webtools/bugzilla/Bugzilla/Bug.pm b/webtools/bugzilla/Bugzilla/Bug.pm index 7ac2bd1012c..7e7478c4674 100644 --- a/webtools/bugzilla/Bugzilla/Bug.pm +++ b/webtools/bugzilla/Bugzilla/Bug.pm @@ -33,7 +33,7 @@ package Bug; use CGI::Carp qw(fatalsToBrowser); my %ok_field; -for my $key (qw (bug_id product version rep_platform op_sys bug_status +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 @@ -74,9 +74,12 @@ sub initBug { my $self = shift(); my ($bug_id, $user_id) = (@_); + # If the bug ID isn't numeric, it might be an alias, so try to convert it. + $bug_id = &::BugAliasToID($bug_id) if $bug_id !~ /^[1-9][0-9]*$/; + my $old_bug_id = $bug_id; if ((! defined $bug_id) || (!$bug_id) || (!&::detaint_natural($bug_id))) { - # no bug number given + # no bug number given or the alias didn't match a bug $self->{'bug_id'} = $old_bug_id; $self->{'error'} = "InvalidBugId"; return $self; @@ -108,7 +111,7 @@ sub initBug { my $query = " select - bugs.bug_id, product, version, rep_platform, op_sys, bug_status, + bugs.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, date_format(creation_ts,'%Y-%m-%d %H:%i'), @@ -123,7 +126,7 @@ sub initBug { if (@row = &::FetchSQLData()) { my $count = 0; my %fields; - foreach my $field ("bug_id", "product", "version", "rep_platform", + 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", @@ -248,7 +251,7 @@ sub emitXML { $xml .= "\n"; - foreach my $field ("bug_id", "bug_status", "product", + foreach my $field ("bug_id", "alias", "bug_status", "product", "priority", "version", "rep_platform", "assigned_to", "delta_ts", "component", "reporter", "target_milestone", "bug_severity", "creation_ts", "qa_contact", "op_sys", "resolution", "bug_file_loc", diff --git a/webtools/bugzilla/CGI.pl b/webtools/bugzilla/CGI.pl index 0b9935602d9..8e8da587125 100644 --- a/webtools/bugzilla/CGI.pl +++ b/webtools/bugzilla/CGI.pl @@ -248,22 +248,55 @@ sub CheckFormFieldDefined (\%$) { } } +sub BugAliasToID { + # Queries the database for the bug with a given alias, and returns + # the ID of the bug if it exists or the undefined value if it doesn't. + + my ($alias) = @_; + + return undef unless Param("usebugaliases"); + + PushGlobalSQLState(); + SendSQL("SELECT bug_id FROM bugs WHERE alias = " . SqlQuote($alias)); + my $id = FetchOneColumn(); + PopGlobalSQLState(); + + return $id; +} + 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 - $_[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) = @_; - + my ($id, $skip_authorization) = @_; + + # Get rid of white-space around the ID. + $id = trim($id); + + # If the ID isn't a number, it might be an alias, so try to convert it. + if ($id !~ /^[1-9][0-9]*$/) { + $id = BugAliasToID($id); + if (!$id) { + my $html_id = html_quote($_[0]); + my $alias_specific_message = Param("usebugaliases") ? + " (it is neither a bug number nor an alias to a bug number)" : ""; + DisplayError(qq| + The bug number $html_id is invalid$alias_specific_message. + If you are trying to use QuickSearch, you need to enable JavaScript + in your browser. To help us fix this limitation, add your comments + to bug + 70907. + |); + exit; + } + } + + # Modify the calling code's original variable to contain the trimmed, + # 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 @@ -276,6 +309,8 @@ sub ValidateBugID { || DisplayError("Bug #$id does not exist.") && exit; + return if $skip_authorization; + return if CanSeeBug($id, $::userid, $::usergroupset); # The user did not pass any of the authorization tests, which means they diff --git a/webtools/bugzilla/bug_form.pl b/webtools/bugzilla/bug_form.pl index 21adb0e309a..262327f17f6 100644 --- a/webtools/bugzilla/bug_form.pl +++ b/webtools/bugzilla/bug_form.pl @@ -77,7 +77,7 @@ sub show_bug { # Populate the bug hash with the info we get directly from the DB. my $query = " - SELECT bugs.bug_id, product, version, rep_platform, + SELECT bugs.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, @@ -92,7 +92,7 @@ sub show_bug { my $value; my @row = FetchSQLData(); - foreach my $field ("bug_id", "product", "version", "rep_platform", + 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", diff --git a/webtools/bugzilla/bugzilla.dtd b/webtools/bugzilla/bugzilla.dtd index 459755ccd85..681a46f9b54 100644 --- a/webtools/bugzilla/bugzilla.dtd +++ b/webtools/bugzilla/bugzilla.dtd @@ -5,11 +5,12 @@ maintainer CDATA #REQUIRED exporter CDATA #IMPLIED > - + + diff --git a/webtools/bugzilla/checksetup.pl b/webtools/bugzilla/checksetup.pl index f4eff45c715..628902d61f1 100755 --- a/webtools/bugzilla/checksetup.pl +++ b/webtools/bugzilla/checksetup.pl @@ -1134,7 +1134,7 @@ my $drh = DBI->install_driver($db_base) if ($my_db_check) { # Do we have the database itself? - my $sql_want = "3.22.5"; # minimum version of MySQL + my $sql_want = "3.23.6"; # minimum version of MySQL # original DSN line was: # my $dsn = "DBI:$db_base:$my_db_name;$my_db_host;$my_db_port"; @@ -1332,7 +1332,8 @@ $table{bugs} = everconfirmed tinyint not null, reporter_accessible tinyint not null default 1, cclist_accessible tinyint not null default 1, - + alias varchar(20), + index (assigned_to), index (creation_ts), index (delta_ts), @@ -1347,7 +1348,9 @@ $table{bugs} = index (resolution), index (target_milestone), index (qa_contact), - index (votes)'; + index (votes), + + unique(alias)'; $table{cc} = @@ -1564,6 +1567,30 @@ $table{tokens} = # Create tables ########################################################################### +# Figure out if any existing tables are of type ISAM and convert them +# to type MyISAM if so. ISAM tables are deprecated in MySQL 3.23, +# which Bugzilla now requires, and they don't support more than 16 +# indexes per table, which Bugzilla needs. +my $sth = $dbh->prepare("SHOW TABLE STATUS FROM $::db_name"); +$sth->execute; +my @isam_tables = (); +while (my ($name, $type) = $sth->fetchrow_array) { + push(@isam_tables, $name) if $type eq "ISAM"; +} + +if(scalar(@isam_tables)) { + print "One or more of the tables in your existing MySQL database are of type ISAM.\n" . + "ISAM tables are deprecated in MySQL 3.23 and don't support more than 16 indexes\n" . + "per table, which Bugzilla needs. Converting your ISAM tables to type MyISAM:\n\n"; + foreach my $table (@isam_tables) { + print "Converting table $table... "; + $dbh->do("ALTER TABLE $table TYPE = MYISAM"); + print "done.\n"; + } + print "\nISAM->MyISAM table conversion done.\n\n"; +} + + # Get a list of the existing tables (if any) in the database my @tables = map { $_ =~ s/.*\.//; $_ } $dbh->tables; #print 'Tables: ', join " ", @tables, "\n"; @@ -1592,10 +1619,6 @@ while (my ($tabname, $fielddef) = each %table) { or die "Could not create table '$tabname'. Please check your '$db_base' access.\n"; } - - - - ########################################################################### # Populate groups table ########################################################################### @@ -1733,6 +1756,7 @@ AddFDef("delta_ts", "Last changed date", 0); AddFDef("(to_days(now()) - to_days(bugs.delta_ts))", "Days since bug changed", 0); AddFDef("longdesc", "Comment", 0); +AddFDef("alias", "Alias", 0); @@ -1861,7 +1885,7 @@ sub bailout { # this is just in case we get interrupted while getting passwd exit 1; } -my $sth = $dbh->prepare(<<_End_Of_SQL_); +$sth = $dbh->prepare(<<_End_Of_SQL_); SELECT login_name FROM profiles WHERE groupset=9223372036854775807 @@ -2955,6 +2979,14 @@ if (GetFieldDef("logincookies", "hostname")) { AddField("logincookies", "ipaddr", "varchar(40) NOT NULL"); } +# 2002-07-03 myk@mozilla.org bug99203: +# Add a bug alias field to the bugs table so bugs can be referenced by alias +# in addition to ID. +if (!GetFieldDef("bugs", "alias")) { + AddField("bugs", "alias", "VARCHAR(20)"); + $dbh->do("ALTER TABLE bugs ADD UNIQUE (alias)"); +} + # 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/webtools/bugzilla/defparams.pl b/webtools/bugzilla/defparams.pl index f4fd85f6f1c..30a7b5fc0aa 100644 --- a/webtools/bugzilla/defparams.pl +++ b/webtools/bugzilla/defparams.pl @@ -400,6 +400,12 @@ DefParam("usedependencies", "b", 1); +DefParam("usebugaliases", + "Do you wish to use bug aliases, which allow you to assign bugs + an easy-to-remember name by which you can refer to them?", + "b", + 0); + DefParam("webdotbase", "It is possible to show graphs of dependent bugs. You may set this parameter to any of the following: diff --git a/webtools/bugzilla/process_bug.cgi b/webtools/bugzilla/process_bug.cgi index 103085457f7..afed412a374 100755 --- a/webtools/bugzilla/process_bug.cgi +++ b/webtools/bugzilla/process_bug.cgi @@ -101,6 +101,24 @@ if (defined $::FORM{'dup_id'} && $::FORM{'knob'} eq "duplicate") { ValidateComment($::FORM{'comment'}); +# If the bug(s) being modified have dependencies, validate them +# and rebuild the list with the validated values. This is important +# because there are situations where validation changes the value +# instead of throwing an error, f.e. when one or more of the values +# is a bug alias that gets converted to its corresponding bug ID +# during validation. +foreach my $field ("dependson", "blocked") { + if (defined($::FORM{$field}) && $::FORM{$field} ne "") { + my @validvalues; + foreach my $id (split(/[\s,]+/, $::FORM{$field})) { + next unless $id; + ValidateBugID($id, 1); + push(@validvalues, $id); + } + $::FORM{$field} = join(",", @validvalues); + } +} + ###################################################################### # End Data/Security Validation ###################################################################### @@ -497,6 +515,63 @@ foreach my $field ("rep_platform", "priority", "bug_severity", } } +# 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'})) { + my $alias = trim($::FORM{'alias'}); + + # Since aliases are unique (like bug numbers), they can only be changed + # for one bug at a time, so ignore the alias change unless only a single + # bug is being changed. + if (scalar(@idlist) == 1) { + # Validate the alias if the user entered one. + if ($alias ne "") { + # Make sure the alias isn't too long. + if (length($alias) > 20) { + ThrowUserError("Bug aliases cannot be longer than 20 characters. + Please choose a shorter alias."); + } + + # Make sure the alias is unique. + my $escaped_alias = SqlQuote($alias); + SendSQL("SELECT bug_id FROM bugs WHERE alias = $escaped_alias " . + "AND bug_id != $idlist[0]"); + my $id = FetchOneColumn(); + if ($id) { + my $escaped_alias = html_quote($alias); + my $bug_link = GetBugLink($id, "Bug $id"); + ThrowUserError("$bug_link has already taken the alias + $escaped_alias. Please choose another one."); + } + + # Make sure the alias isn't just a number. + if ($alias =~ /^\d+$/) { + ThrowUserError("You gave this bug the alias $alias, + but aliases cannot be merely numbers, since they could + then be confused with bug IDs. Please choose another + alias containing at least one letter."); + } + + # Make sure the alias has no commas or spaces. + if ($alias =~ /[, ]/) { + my $escaped_alias = html_quote($alias); + ThrowUserError("The alias you entered, $escaped_alias, + contains one or more commas or spaces. Aliases cannot contain + commas or spaces because those characters are used to separate + aliases from each other in lists. Please choose another alias + that does not contain commas and spaces."); + } + } + + # Add the alias change to the query. If the field contains the blank + # value, make the field be NULL to indicate that the bug has no alias. + # Otherwise, if the field contains a value, update the record + # with that value. + DoComma(); + $::query .= "alias = "; + $::query .= ($alias eq "") ? "NULL" : SqlQuote($alias); + } +} if (defined $::FORM{'qa_contact'}) { my $name = trim($::FORM{'qa_contact'}); @@ -909,23 +984,8 @@ foreach my $id (@idlist) { $deptree{$target} = []; my %seen; foreach my $i (split('[\s,]+', $::FORM{$target})) { - if ($i eq "") { - next; - } - - my $orig = $i; - if (!detaint_natural($i)) { - ThrowUserError("$orig is not a legal bug number", undef, "abort"); - } - - # 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(); - if ($comp ne $i) { - ThrowUserError("$i is not a legal bug number", undef, "abort"); - } + next if $i eq ""; + if ($id eq $i) { ThrowUserError("You can't make a bug blocked or dependent on itself.", undef, diff --git a/webtools/bugzilla/template/en/default/bug/edit.html.tmpl b/webtools/bugzilla/template/en/default/bug/edit.html.tmpl index 81b0a15bf60..f88fb4002d4 100644 --- a/webtools/bugzilla/template/en/default/bug/edit.html.tmpl +++ b/webtools/bugzilla/template/en/default/bug/edit.html.tmpl @@ -49,6 +49,12 @@ [% bug.bug_id %] + [% IF Param("usebugaliases") %] + + [% END %]