#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # 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) 1998 Netscape Communications Corporation. All # Rights Reserved. # # Contributor(s): Terry Weissman # Andreas Franke # Christian Reis # Myk Melez use strict; use lib qw(.); require "CGI.pl"; use Bugzilla::User; use Bugzilla::Bug; # Use global template variables. use vars qw($template $vars); Bugzilla->login(); my $cgi = Bugzilla->cgi; # Connect to the shadow database if this installation is using one to improve # performance. Bugzilla->switch_to_shadow_db(); # More warning suppression silliness. $::userid = $::userid; ################################################################################ # Data/Security Validation # ################################################################################ # Make sure the bug ID is a positive integer representing an existing # bug that the user is authorized to access. my $id = $cgi->param('id'); ValidateBugID($id); my $hide_resolved = $cgi->param('hide_resolved') ? 1 : 0; my $maxdepth = $cgi->param('maxdepth') || 0; if ($maxdepth !~ /^\d+$/) { $maxdepth = 0 }; ################################################################################ # Main Section # ################################################################################ # The column/value to select as the target milestone for bugs, # either the target_milestone column or the empty string value # (for installations that don't use target milestones). Makes # it easier to query the database for bugs because we don't # have to embed a conditional statement into each query. my $milestone_column = Param('usetargetmilestone') ? "target_milestone" : "''"; # The greatest depth to which either tree goes. my $realdepth = 0; # Generate the tree of bugs that this bug depends on and a list of IDs # appearing in the tree. my $dependson_tree = { $id => GetBug($id) }; my $dependson_ids = {}; GenerateTree($id, "dependson", 1, $dependson_tree, $dependson_ids); $vars->{'dependson_tree'} = $dependson_tree; $vars->{'dependson_ids'} = [keys(%$dependson_ids)]; # Generate the tree of bugs that this bug blocks and a list of IDs # appearing in the tree. my $blocked_tree = { $id => GetBug($id) }; my $blocked_ids = {}; GenerateTree($id, "blocked", 1, $blocked_tree, $blocked_ids); $vars->{'blocked_tree'} = $blocked_tree; $vars->{'blocked_ids'} = [keys(%$blocked_ids)]; $vars->{'realdepth'} = $realdepth; $vars->{'bugid'} = $id; $vars->{'maxdepth'} = $maxdepth; $vars->{'hide_resolved'} = $hide_resolved; $vars->{'canedit'} = UserInGroup("editbugs"); print $cgi->header(); $template->process("bug/dependency-tree.html.tmpl", $vars) || ThrowTemplateError($template->error()); ################################################################################ # Recursive Tree Generation Function # ################################################################################ sub GenerateTree { # Generates a dependency tree for a given bug. Calls itself recursively # to generate sub-trees for the bug's dependencies. my ($bug_id, $relationship, $depth, $bugs, $ids) = @_; # Query the database for bugs with the given dependency relationship. my @dependencies = GetDependencies($bug_id, $relationship); # Don't do anything if this bug doesn't have any dependencies. return unless scalar(@dependencies); # Record this depth in the global $realdepth variable if it's farther # than we've gone before. $realdepth = max($realdepth, $depth); foreach my $dep_id (@dependencies) { # Get this dependency's record from the database and generate # its sub-tree if we haven't already done so (which happens # when bugs appear in dependency trees multiple times). if (!$bugs->{$dep_id}) { $bugs->{$dep_id} = GetBug($dep_id); GenerateTree($dep_id, $relationship, $depth+1, $bugs, $ids); } # Add this dependency to the list of this bug's dependencies # if it exists, if we haven't exceeded the maximum depth the user # wants the tree to go, and if the dependency isn't resolved # (if we're ignoring resolved dependencies). if ($bugs->{$dep_id}->{'exists'} && (!$maxdepth || $depth <= $maxdepth) && ($bugs->{$dep_id}->{'open'} || !$hide_resolved)) { push (@{$bugs->{$bug_id}->{'dependencies'}}, $dep_id); $ids->{$dep_id} = 1; } } } sub GetBug { # Retrieves the necessary information about a bug, stores it in the bug cache, # and returns it to the calling code. my ($id) = @_; my $bug = {}; if (Bugzilla->user->can_see_bug($id)) { SendSQL("SELECT 1, bug_status, short_desc, $milestone_column, assignee.userid, assignee.login_name FROM bugs INNER JOIN profiles AS assignee ON bugs.assigned_to = assignee.userid WHERE bugs.bug_id = $id"); ($bug->{'exists'}, $bug->{'status'}, $bug->{'summary'}, $bug->{'milestone'}, $bug->{'assignee_id'}, $bug->{'assignee_email'}) = FetchSQLData(); } $bug->{'open'} = $bug->{'exists'} && IsOpenedState($bug->{'status'}); $bug->{'dependencies'} = []; return $bug; } sub GetDependencies { # Returns a list of dependencies for a given bug. my ($id, $relationship) = @_; my $bug_type = ($relationship eq "blocked") ? "dependson" : "blocked"; SendSQL(" SELECT $relationship FROM dependencies WHERE $bug_type = $id ORDER BY $relationship"); my @dependencies = (); push(@dependencies, FetchOneColumn()) while MoreSQLData(); return @dependencies; }