From 355cb9728d83a04e420501fd8402e88fb08daf1f Mon Sep 17 00:00:00 2001 From: "myk%mozilla.org" <> Date: Thu, 18 Apr 2002 04:58:26 +0000 Subject: Fix for bug 126792: Templatizes showdependencytree.cgi. Patch by Myk Melez . r=afranke,gerv --- showdependencytree.cgi | 484 +++++++++++++------------------------------------ 1 file changed, 128 insertions(+), 356 deletions(-) (limited to 'showdependencytree.cgi') diff --git a/showdependencytree.cgi b/showdependencytree.cgi index b19302295..0288faaa1 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -21,6 +21,7 @@ # Contributor(s): Terry Weissman # Andreas Franke # Christian Reis +# Myk Melez use diagnostics; use strict; @@ -28,6 +29,9 @@ use strict; use lib qw(.); require "CGI.pl"; +# Use global template variables. +use vars qw($template $vars); + use vars %::FORM; ConnectToDatabase(); @@ -38,384 +42,152 @@ quietly_check_login(); $::userid = $::userid; $::usergroupset = $::usergroupset; -###################################################################### -# Begin Data/Security Validation -###################################################################### +################################################################################ +# Data/Security Validation # +################################################################################ # Make sure the bug ID is a positive integer representing an existing -# bug that the user is authorized to access +# bug that the user is authorized to access. ValidateBugID($::FORM{'id'}); - my $id = $::FORM{'id'}; -my $hide_resolved = $::FORM{'hide_resolved'} || 0; -my $maxdepth = $::FORM{'maxdepth'} || 0; - -if ($maxdepth !~ /^\d+$/) { $maxdepth = 0 }; -if ($hide_resolved !~ /^\d+$/ || $hide_resolved != 1) { $hide_resolved = 0 }; -###################################################################### -# End Data/Security Validation -###################################################################### +my $hide_resolved = $::FORM{'hide_resolved'} ? 1 : 0; -# -# Globals - -# A hash to count visited bugs, and also to avoid processing repeated bugs -my %seen; +my $maxdepth = $::FORM{'maxdepth'} || 0; +if ($maxdepth !~ /^\d+$/) { $maxdepth = 0 }; -# A hash to keep track of the bugs we print for the 'as buglist' links. -my %printed; +################################################################################ +# Main Section # +################################################################################ -# HTML output generated in the parse of the dependency tree. This is a -# global only to avoid excessive complication in the recursion invocation -my $html; +# 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" : "''"; -# Saves the largest of the two actual depths of the trees +# The greatest depth to which either tree goes. my $realdepth = 0; -# The scriptname for use as FORM ACTION. -my $scriptname = $::ENV{'SCRIPT_NAME'}; # showdependencytree.cgi - -# -# Functions - -# DumpKids recurses through the bug hierarchy starting at bug i, and -# appends the bug information found to the html global variable. The -# parameters are not straightforward, so look at the examples. -# -# DumpKids(i, target [, depth]) -# -# Params -# i: The bug id to analyze -# target: The type we are looking for; either "blocked" or "dependson" -# Optional -# depth: The current dependency depth we are analyzing, used during -# recursion -# Globals Modified -# html: Bug descriptions are appended here -# realdepth: We set the maximum depth of recursion reached -# seen: We store the bugs analyzed so far -# printed: We store those bugs we actually print, for the "buglist" link -# Globals Referenced -# maxdepth -# hide_resolved -# -# Examples: -# DumpKids(163, "blocked"); -# will look for bugs that depend on bug 163 -# DumpKids(163, "dependson"); -# will look for bugs on which bug 163 depends - -sub DumpKids { - my ($i, $target, $depth) = (@_); - my $bgcolor = "#d9d9d9"; - my $fgcolor = "#000000"; - my $me; - if (! defined $depth) { $depth = 1; } - - if ($target eq "blocked") { - $me = "dependson"; - } else { - $me = "blocked"; - } - SendSQL("select $target from dependencies where $me=$i order by $target"); - my @list; - while (MoreSQLData()) { - push(@list, FetchOneColumn()); - } - if (@list) { - my $list_started = 0; - foreach my $kid (@list) { - my ($bugid, $stat, $milestone) = ("", "", ""); - my ($userid, $short_desc) = ("", ""); - if (Param('usetargetmilestone')) { - SendSQL(SelectVisible("select bugs.bug_id, bug_status, target_milestone, assigned_to, short_desc from bugs where bugs.bug_id = $kid", $::userid, $::usergroupset)); - ($bugid, $stat, $milestone, $userid, $short_desc) = (FetchSQLData()); - } else { - SendSQL(SelectVisible("select bugs.bug_id, bug_status, assigned_to, short_desc from bugs where bugs.bug_id = $kid", $::userid, $::usergroupset)); - ($bugid, $stat, $userid, $short_desc) = (FetchSQLData()); - - } - if (! defined $bugid) { next; } - my $opened = IsOpenedState($stat); - if ($hide_resolved && ! $opened) { next; } - - # If we specify a maximum depth, we hide the output when - # that depth has occured, but continue recursing so we know - # the real maximum depth of the tree. - if (! $maxdepth || $depth <= $maxdepth) { - if (! $list_started) { $html .= "
    "; $list_started = 1 } - $html .= "
  • "; - if (! $opened) { - $html .= qq| - |; - } - - if (exists $printed{$kid}) { - $short_desc = "<This bug appears elsewhere in this tree>"; - } - else { - $short_desc = html_quote($short_desc); - } - - SendSQL("select login_name from profiles where userid = $userid"); - my ($owner) = (FetchSQLData()); - if ((Param('usetargetmilestone')) && ($milestone)) { - $html .= qq| - $kid [$milestone, $owner] - - $short_desc. - |; - } else { - $html .= qq| - $kid [$owner] - - $short_desc.\n|; - } - if (! $opened) { $html .= ""; } - - $printed{$kid} = 1; - } # End hideable output +# 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 "Content-Type: text/html\n\n"; +$template->process("show/dependency-tree.html.tmpl", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; + +################################################################################ +# 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); + } - # Store the maximum depth so far - $realdepth = $realdepth < $depth ? $depth : $realdepth; - if (!(exists $seen{$kid})) { - $seen{$kid} = 1; - DumpKids($kid, $target, $depth + 1); - } + # 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; } - if ($list_started) { $html .= "
"; } } } -# makeTreeHTML calls DumpKids and generates the HTML output for a -# dependency/blocker section. -# -# makeTreeHTML(i, linked_id, target); -# -# Params -# i: Bug id -# linked_id: Linkified bug_id used to linkify the bug -# target: The type we are looking for; either "blocked" or "dependson" -# Globals modified -# html [Also modified in our call to DumpKids] -# Globals referenced -# seen [Also modified by DumpKids] -# maxdepth -# realdepth -# -# Example: -# $depend_html = makeTreeHTML(83058, 83058, "dependson"); -# Will generate HTML for bugs that depend on bug 83058 - -sub makeTreeHTML { - my ($i, $linked_id, $target) = @_; - - # Clean up globals for this run - $html = ""; - %seen = (); - %printed = (); - - DumpKids($i, $target); - my $tmphtml = $html; - - # Output correct heading - $html = "

Bugs that bug $linked_id ".($target eq "blocked" ? - "blocks" : "depends on"); - - # If there are some bugs in this tree, add a link to view them - # as a bug list, and add another link to edit them on the "change - # several bugs" page if there is more than one and the user has - # "editbugs" privileges. - if ((scalar keys %printed) > 0) { - my $link = "buglist.cgi?bug_id=" . join(',', keys %printed); - $html .= qq~ (view as bug list~; - if (UserInGroup("editbugs") && (scalar keys %printed) > 1) { - $html .= qq~ | change several~; - } - $html .= ')'; - } +sub GetBug { + # Retrieves the necessary information about a bug, stores it in the bug cache, + # and returns it to the calling code. + my ($id) = @_; - # Provide feedback for omitted bugs - if ($maxdepth || $hide_resolved) { - $html .= " (Only "; - if ($hide_resolved) { $html .= "open "; } - $html .= "bugs "; - if ($maxdepth) { $html .= "whose depth is less than $maxdepth "; } - $html .= "will be shown)"; - } - - $html .= "

"; - $html .= $tmphtml; - - # If no bugs were found, say so - if ((scalar keys %printed) == 0) { - $html .= "    None

\n"; - } - - return $html; + SendSQL(SelectVisible("SELECT 1, + bug_status, + short_desc, + $milestone_column, + assignee.userid, + assignee.login_name + FROM bugs, profiles AS assignee + WHERE bugs.bug_id = $id + AND bugs.assigned_to = assignee.userid", + $::userid, + $::usergroupset)); + + my $bug = {}; + + ($bug->{'exists'}, + $bug->{'status'}, + $bug->{'summary'}, + $bug->{'milestone'}, + $bug->{'assignee_id'}, + $bug->{'assignee_email'}) = FetchSQLData(); + + $bug->{'open'} = IsOpenedState($bug->{'status'}); + $bug->{'dependencies'} = (); + + return $bug; } -# Draw the actual form controls that make up the hide/show resolved and -# depth control toolbar. -# -# drawDepForm() -# -# Params -# none -# Globals modified -# none -# Globals referenced -# hide_resolved -# maxdepth -# realdepth - -sub drawDepForm { - my $bgcolor = "#d0d0d0"; - my ($hide_msg, $hide_input); - - # Set the text and action for the hide resolved button. - if ($hide_resolved) { - $hide_input = ''; - $hide_msg = "Show Resolved"; - } else { - $hide_input = ''; - $hide_msg = "Hide Resolved"; - } - - print qq| - - - - - - - - - - - - -
-
- - | . ( $maxdepth ? - qq|| - : "" ) . qq| - $hide_input - - -
-
- - - Max Depth: +sub GetDependencies { + # Returns a list of dependencies for a given bug. - - - -
+ my ($id, $relationship) = @_; - - - - - -
-
-
- - - - - - - -
-
-
- - - - - - - -
-
-
+ my $bug_type = ($relationship eq "blocked") ? "dependson" : "blocked"; - - - | . ( $maxdepth ? qq| - | : "" ) - . qq| - - = $realdepth ? - "disabled" : "" ) . qq|> - -
-
-
+ SendSQL(" SELECT $relationship + FROM dependencies + WHERE $bug_type = $id + ORDER BY $relationship"); - - - - - -
-
- |; + my @dependencies = (); + push(@dependencies, FetchOneColumn()) while MoreSQLData(); + + return @dependencies; } -###################################################################### -# Main Section -###################################################################### - -my $linked_id = qq|$id|; - -# Start the tree walk and save results. The tree walk generates HTML but -# needs to be called before the page output starts so we have the -# realdepth, which is necessary for generating the control toolbar. - -# Get bugs we depend on -my $depend_html = makeTreeHTML($id, $linked_id, "dependson"); - -my $tmpdepth = $realdepth; -$realdepth = 0; - -# Get bugs we block -my $block_html = makeTreeHTML($id, $linked_id, "blocked"); - -# Select maximum depth found for use in the toolbar -$realdepth = $realdepth < $tmpdepth ? $tmpdepth : $realdepth; - -# -# Actual page output happens here - -print "Content-type: text/html\n\n"; -PutHeader("Dependency tree for Bug $id", "Dependency tree for Bug $linked_id"); -drawDepForm(); -print $depend_html; -print $block_html; -drawDepForm(); -PutFooter(); -- cgit v1.2.3-24-g4f1b