=== modified file 'Bugzilla/Bug.pm' --- Bugzilla/Bug.pm 2010-05-14 12:25:20 +0000 +++ Bugzilla/Bug.pm 2010-07-21 16:12:01 +0000 @@ -3046,6 +3046,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 ##################################################################### === modified file 'Bugzilla/Constants.pm' --- Bugzilla/Constants.pm 2010-06-24 23:14:56 +0000 +++ Bugzilla/Constants.pm 2010-07-21 16:12:01 +0000 @@ -140,6 +140,7 @@ ERROR_MODE_WEBPAGE ERROR_MODE_DIE ERROR_MODE_DIE_SOAP_FAULT + ERROR_MODE_AJAX ERROR_MODE_JSON_RPC INSTALLATION_MODE_INTERACTIVE @@ -421,6 +422,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 => 4; use constant ERROR_MODE_JSON_RPC => 3; # The various modes that checksetup.pl can run in. === modified file 'Bugzilla/Error.pm' --- Bugzilla/Error.pm 2010-04-01 01:06:03 +0000 +++ Bugzilla/Error.pm 2010-07-21 16:12: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! @@ -135,6 +136,16 @@ $server->response($server->error_response_header); } } + 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; } === modified file 'Bugzilla/Install/Filesystem.pm' --- Bugzilla/Install/Filesystem.pm 2010-07-13 23:03:47 +0000 +++ Bugzilla/Install/Filesystem.pm 2010-07-21 16:12:01 +0000 @@ -132,6 +132,7 @@ 'runtests.pl' => { perms => $owner_executable }, 'testserver.pl' => { perms => $ws_executable }, 'whine.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 }, === modified file 'Bugzilla/Search.pm' --- Bugzilla/Search.pm 2010-07-05 23:39:24 +0000 +++ Bugzilla/Search.pm 2010-07-21 16:12:01 +0000 @@ -274,6 +274,12 @@ push(@supptables, "LEFT JOIN longdescs AS ldtime " . "ON ldtime.bug_id = bugs.bug_id"); } + ### Testopia ### + if (grep($_ eq 'test_cases', @fields)){ + push(@supptables, "LEFT JOIN test_case_bugs AS tcb " . + "ON bugs.bug_id = tcb.bug_id "); + } + ### end Testopia ### if (grep($_ eq 'flagtypes.name', @fields)) { push(@supptables, "LEFT JOIN flags ON flags.bug_id = bugs.bug_id AND attach_id IS NULL"); === modified file 'Bugzilla/User.pm' --- Bugzilla/User.pm 2010-02-18 00:16:31 +0000 +++ Bugzilla/User.pm 2010-07-21 16:12:01 +0000 @@ -361,6 +361,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) = @_; @@ -688,16 +698,23 @@ 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 " . " AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY . " AND group_id NOT IN(" . $self->groups_as_string . ") " . - " WHERE group_id IS NULL " . - "ORDER BY name"; - - my $prod_ids = Bugzilla->dbh->selectcol_arrayref($query); + " WHERE group_id IS NULL) " ; + + $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); } @@ -990,6 +1007,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 { === modified file 'template/en/default/global/setting-descs.none.tmpl' --- template/en/default/global/setting-descs.none.tmpl 2009-08-19 21:40:07 +0000 +++ template/en/default/global/setting-descs.none.tmpl 2010-07-21 16:12:07 +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",