]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Fix for bug 99203: Implements bug aliases feature.
authormyk%mozilla.org <>
Thu, 4 Jul 2002 06:07:09 +0000 (06:07 +0000)
committermyk%mozilla.org <>
Thu, 4 Jul 2002 06:07:09 +0000 (06:07 +0000)
r=bbaetz,jouni

Bug.pm
Bugzilla/Bug.pm
CGI.pl
bug_form.pl
bugzilla.dtd
checksetup.pl
defparams.pl
process_bug.cgi
template/en/default/bug/edit.html.tmpl

diff --git a/Bug.pm b/Bug.pm
index 7ac2bd1012c7b6074567dbd13890c3602d2d7b89..7e7478c46743ebc4d189d41ce6472a7208e8287c 100755 (executable)
--- a/Bug.pm
+++ b/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 .= "<bug>\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",
index 7ac2bd1012c7b6074567dbd13890c3602d2d7b89..7e7478c46743ebc4d189d41ce6472a7208e8287c 100755 (executable)
@@ -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 .= "<bug>\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/CGI.pl b/CGI.pl
index 0b9935602d9bf0559285200b87f2ca273a3660b1..8e8da58712560934e90d8a9db38bec0af601aade 100644 (file)
--- a/CGI.pl
+++ b/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 " .
-                      "<a href=\"http://bugzilla.mozilla.org/show_bug.cgi?id=70907\">here</a>.") 
-      && 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 <em>$html_id</em> 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 <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=70907">bug 
+              70907</a>.
+            |);
+            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
index 21adb0e309aecd9a728f42066fa8d4eea8ab7eb4..262327f17f6beee1fcd6a8a097b12fd7087e9cee 100644 (file)
@@ -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",
index 459755ccd854fa3a566b01e0b07ad423ba85fee2..681a46f9b54f52000e53ac16cfeba378464e520e 100644 (file)
@@ -5,11 +5,12 @@
        maintainer CDATA #REQUIRED
        exporter CDATA #IMPLIED
 >
-<!ELEMENT bug (bug_id, (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?, short_desc?, keywords*, status_whiteboard?, dependson*, blocks*, cc*, long_desc*, attachment*)?)>
+<!ELEMENT bug (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?, short_desc?, keywords*, status_whiteboard?, dependson*, blocks*, cc*, long_desc*, attachment*)?)>
 <!ATTLIST bug
        error (NotFound | NotPermitted | InvalidBugId) #IMPLIED
 >
 <!ELEMENT bug_id (#PCDATA)>
+<!ELEMENT alias (#PCDATA)>
 <!ELEMENT exporter (#PCDATA)>
 <!ELEMENT urlbase (#PCDATA)>
 <!ELEMENT bug_status (#PCDATA)>
index f4eff45c71586f27d1f0f909827d15c7ede88b0f..628902d61f1cb6217856741715e89e9b7b7b4853 100755 (executable)
@@ -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.
 #
index f4fd85f6f1c3208e30d077ffc5148e3e59916b5c..30a7b5fc0aa8b8c533c13825b48ed2444ab72e24 100644 (file)
@@ -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:
index 103085457f70b456c9ec6ca20e11cd5c10723747..afed412a37421c46ca37b94a636d6d5027b93d6b 100755 (executable)
@@ -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 
+                  <em>$escaped_alias</em>.  Please choose another one.");
+            }
+
+            # Make sure the alias isn't just a number.
+            if ($alias =~ /^\d+$/) {
+                ThrowUserError("You gave this bug the alias <em>$alias</em>,
+                  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, <em>$escaped_alias</em>,
+                  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,
index 81b0a15bf60b7d8f766f5de6f7f97121f8999077..f88fb4002d4d2d89d61eef930188624310585193 100644 (file)
       <td>
         <a href="[% Param('urlbase') %]show_bug.cgi?id=[% bug.bug_id %]">
           [% bug.bug_id %]</a>
+          [% IF Param("usebugaliases") %]
+            <label title="a name for the bug that can be used in place of its ID number, f.e. when adding it to a list of dependencies">
+              <b>alias:</b>
+              <input name="alias" value="[% bug.alias FILTER html %]" size="20" maxlength="20">
+            </label>
+          [% END %]
       </td>
       
       <td>&nbsp;</td>