Index: Bugzilla/WebService/User.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/WebService/User.pm,v
retrieving revision 1.6.2.2
diff -u -r1.6.2.2 User.pm
--- Bugzilla/WebService/User.pm 26 Nov 2008 01:24:15 -0000  1.6.2.2
+++ Bugzilla/WebService/User.pm 18 Dec 2008 22:33:02 -0000
@@ -125,6 +125,32 @@
     return { id => type('int')->value($user->id) };
 }
 
+#################
+# User Lookup   #
+#################
+
+sub lookup_login_by_id {
+  my $self = shift;
+  my ($author_id) = @_;
+
+  my $user = new Bugzilla::User($author_id);
+
+  my $result = defined $user ? $user->login : '';
+  
+  # Result is user login string or empty string if failed
+  return $result;
+}
+
+sub lookup_id_by_login {
+  my $self = shift;
+  my ($author) = @_;
+
+  my $result = Bugzilla::User::login_to_id($author);
+  
+  # Result is user id or 0 if failed
+  return $result;
+}
+
 1;
 
 __END__
Index: Bugzilla/Constants.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Constants.pm,v
retrieving revision 1.92.2.7
diff -u -r1.92.2.7 Constants.pm
--- Bugzilla/Constants.pm   30 Nov 2008 01:34:57 -0000  1.92.2.7
+++ Bugzilla/Constants.pm   18 Dec 2008 22:33:01 -0000
@@ -131,6 +131,7 @@
     ERROR_MODE_WEBPAGE
     ERROR_MODE_DIE
     ERROR_MODE_DIE_SOAP_FAULT
+    ERROR_MODE_AJAX
 
     INSTALLATION_MODE_INTERACTIVE
     INSTALLATION_MODE_NON_INTERACTIVE
@@ -370,6 +371,7 @@
 use constant ERROR_MODE_WEBPAGE        => 0;
 use constant ERROR_MODE_DIE            => 1;
 use constant ERROR_MODE_DIE_SOAP_FAULT => 2;
+use constant ERROR_MODE_AJAX           => 3;
 
 # The various modes that checksetup.pl can run in.
 use constant INSTALLATION_MODE_INTERACTIVE => 0;
Index: Bugzilla/Bug.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Bug.pm,v
retrieving revision 1.241.2.18
diff -u -r1.241.2.18 Bug.pm
--- Bugzilla/Bug.pm 19 Nov 2008 19:32:57 -0000  1.241.2.18
+++ Bugzilla/Bug.pm 18 Dec 2008 22:33:01 -0000
@@ -2709,6 +2709,14 @@
         "SELECT bug_id FROM bugs WHERE alias = ?", undef, $alias);
 }
 
+sub get_test_case_count {
+      my $self = shift;
+      my $dbh = Bugzilla->dbh;
+      my $row_count = $dbh->selectall_arrayref(
+              "SELECT DISTINCT case_id FROM test_case_bugs WHERE bug_id = ?",
+              undef, $self->bug_id);
+      return scalar @$row_count;
+}
 #####################################################################
 # Subroutines
 #####################################################################
Index: Bugzilla/Error.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Error.pm,v
retrieving revision 1.23.2.1
diff -u -r1.23.2.1 Error.pm
--- Bugzilla/Error.pm   27 May 2008 22:11:56 -0000  1.23.2.1
+++ Bugzilla/Error.pm   18 Dec 2008 22:33:01 -0000
@@ -32,6 +32,7 @@
 use Bugzilla::WebService::Constants;
 use Bugzilla::Util;
 use Date::Format;
+use JSON;
 
 # We cannot use $^S to detect if we are in an eval(), because mod_perl
 # already eval'uates everything, so $^S = 1 in all cases under mod_perl!
@@ -114,6 +115,16 @@
             }
             die SOAP::Fault->faultcode($code)->faultstring($message);
         }
+        elsif (Bugzilla->error_mode == ERROR_MODE_AJAX) {
+            # JSON can't handle strings across lines. 
+            $message =~ s/\n/ /gm;
+            my $err;
+            $err->{'success'} = JSON::false;
+            $err->{'error'} = $error;
+            $err->{'message'} = $message;
+            my $json = new JSON;
+            print $json->encode($err);
+        }
     }
     exit;
 }
Index: Bugzilla/User.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/User.pm,v
retrieving revision 1.164.2.4
diff -u -r1.164.2.4 User.pm
--- Bugzilla/User.pm    8 Nov 2008 19:03:32 -0000   1.164.2.4
+++ Bugzilla/User.pm    18 Dec 2008 22:33:02 -0000
@@ -335,6 +335,16 @@
     return $self->{queries_available};
 }
 
+sub testopia_queries {
+    my $self = shift;
+    my $dbh = Bugzilla->dbh;
+    my $ref = $dbh->selectall_arrayref(
+        "SELECT name, query FROM test_named_queries
+         WHERE userid = ? AND isvisible = 1",
+         {'Slice' =>{}}, $self->id);
+    return $ref;
+}
+
 sub settings {
     my ($self) = @_;
 
@@ -632,7 +642,7 @@
     my $class_restricted = Bugzilla->params->{'useclassification'} && $class_id;
 
     if (!defined $self->{selectable_products}) {
-        my $query = "SELECT id " .
+        my $query = "(SELECT id, name AS pname " .
                     "  FROM products " .
                  "LEFT JOIN group_control_map " .
                     "    ON group_control_map.product_id = products.id ";
@@ -642,10 +652,17 @@
             $query .= " AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY;
         }
         $query .= "     AND group_id NOT IN(" . $self->groups_as_string . ") " .
-                  "   WHERE group_id IS NULL " .
-                  "ORDER BY name";
+                  "   WHERE group_id IS NULL) " ;
+                  
 
-        my $prod_ids = Bugzilla->dbh->selectcol_arrayref($query);
+        $query .= "UNION (SELECT id, tr_products.name AS pname FROM products AS tr_products ".
+                  "INNER JOIN test_plans ON tr_products.id = test_plans.product_id ".
+                  "INNER JOIN test_plan_permissions ON test_plan_permissions.plan_id = test_plans.plan_id ".
+                  "WHERE test_plan_permissions.userid = ?)";
+        
+        $query .= "ORDER BY pname ";    
+
+        my $prod_ids = Bugzilla->dbh->selectcol_arrayref($query,undef,$self->id);
         $self->{selectable_products} = Bugzilla::Product->new_from_list($prod_ids);
     }
 
@@ -925,6 +942,33 @@
             $group_delete->execute($id, $group, GRANT_REGEXP) if $present;
         }
     }
+    # Now do the same for Testopia test plans.
+    $sth = $dbh->prepare("SELECT test_plan_permissions_regexp.plan_id, 
+                                 user_regexp, test_plan_permissions_regexp.permissions, 
+                                 test_plan_permissions.plan_id
+                          FROM test_plan_permissions_regexp
+                     LEFT JOIN test_plan_permissions 
+                            ON test_plan_permissions_regexp.plan_id = test_plan_permissions.plan_id
+                           AND test_plan_permissions.userid = ?
+                           AND test_plan_permissions.grant_type = ?");
+    
+    $sth->execute($id, GRANT_REGEXP);
+    my $plan_insert = $dbh->prepare(q{INSERT INTO test_plan_permissions
+                                       (userid, plan_id, permissions, grant_type)
+                                       VALUES (?, ?, ?, ?)});
+    my $plan_delete = $dbh->prepare(q{DELETE FROM test_plan_permissions
+                                       WHERE userid = ?
+                                         AND plan_id = ?
+                                         AND grant_type = ?});
+    
+    while (my ($planid, $regexp, $perms, $present) = $sth->fetchrow_array()) {
+        if (($regexp ne '') && ($self->{login} =~ m/$regexp/i)) {
+            $plan_insert->execute($id, $planid, $perms, GRANT_REGEXP) unless $present;
+        } else {
+            $plan_delete->execute($id, $planid, GRANT_REGEXP) if $present;
+        }
+    }
+    
 }
 
 sub product_responsibilities {
Index: Bugzilla/DB/Mysql.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/DB/Mysql.pm,v
retrieving revision 1.60.2.7
diff -u -r1.60.2.7 Mysql.pm
--- Bugzilla/DB/Mysql.pm    7 Nov 2008 00:10:15 -0000   1.60.2.7
+++ Bugzilla/DB/Mysql.pm    18 Dec 2008 22:33:02 -0000
@@ -690,6 +690,9 @@
                             $self->bz_drop_index('bugs_fulltext', $index);
                         }
                     }
+                    if ($table eq 'test_runs' && $name eq 'summary') {
+                        $self->bz_drop_index('test_runs', 'test_runs_summary_idx');
+                    }
 
                     print "Converting $table.$name to be stored as UTF-8...\n";
                     my $col_info = 
Index: Bugzilla/Install/Filesystem.pm
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Install/Filesystem.pm,v
retrieving revision 1.29.2.1
diff -u -r1.29.2.1 Filesystem.pm
--- Bugzilla/Install/Filesystem.pm  13 Jun 2008 20:58:47 -0000  1.29.2.1
+++ Bugzilla/Install/Filesystem.pm  18 Dec 2008 22:33:02 -0000
@@ -111,6 +111,8 @@
         'runtests.pl'     => { perms => $owner_executable },
         'testserver.pl'   => { perms => $ws_executable },
         'whine.pl'        => { perms => $ws_executable },
+        'tr_csv2xml.pl'   => { perms => $ws_executable },
+        'tr_importxml.pl' => { perms => $ws_executable },
         'customfield.pl'  => { perms => $owner_executable },
         'email_in.pl'     => { perms => $ws_executable },
         'sanitycheck.pl'  => { perms => $ws_executable },
@@ -157,6 +159,8 @@
                                      dirs => $ws_dir_readable },
          $extlib               => { files => $ws_readable,
                                      dirs => $ws_dir_readable },
+         "$libdir/testopia"    => { files => $ws_readable,
+                                     dirs => $ws_dir_readable },
          $templatedir          => { files => $ws_readable,
                                      dirs => $ws_dir_readable },
          $extensionsdir        => { files => $ws_readable,
@@ -171,6 +175,8 @@
                                      dirs => $ws_dir_readable },
          t                     => { files => $owner_readable,
                                      dirs => $owner_dir_readable },
+         'testopia/t'          => { files => $owner_readable,
+                                      dirs => $owner_dir_readable },
          'docs/*/html'         => { files => $ws_readable,
                                      dirs => $ws_dir_readable },
          'docs/*/pdf'          => { files => $ws_readable,
Index: template/en/default/admin/products/confirm-delete.html.tmpl
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/template/en/default/admin/products/confirm-delete.html.tmpl,v
retrieving revision 1.9
diff -u -r1.9 confirm-delete.html.tmpl
--- template/en/default/admin/products/confirm-delete.html.tmpl 7 Oct 2007 23:18:29 -0000   1.9
+++ template/en/default/admin/products/confirm-delete.html.tmpl 18 Dec 2008 22:33:02 -0000
@@ -255,6 +255,8 @@
 
 [% END %]
 
+[% Hook.process("additional-product-values") %]
+
 [% IF product.bug_count == 0 || Param('allowbugdeletion') %]
 
   <p>Do you really want to delete this product?</p>
Index: template/en/default/global/setting-descs.none.tmpl
===================================================================
RCS file: /cvsroot/mozilla/webtools/bugzilla/template/en/default/global/setting-descs.none.tmpl,v
retrieving revision 1.14
diff -u -r1.14 setting-descs.none.tmpl
--- template/en/default/global/setting-descs.none.tmpl  20 Aug 2007 18:25:04 -0000  1.14
+++ template/en/default/global/setting-descs.none.tmpl  18 Dec 2008 22:33:02 -0000
@@ -40,6 +40,7 @@
    "never"                            => "Never",
    "cc_unless_role"                   => "Only if I have no role on them",
    "lang"                             => "Language used in email",
+   "view_testopia"                    => "View the Testopia links",
    "quote_replies"                    => "Quote the associated comment when you click on its reply link",
    "quoted_reply"                     => "Quote the full comment",
    "simple_reply"                     => "Reference the comment number only",
