##!/usr/bonsaitools/bin/perl -w # -*- 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): Rick Dean # Terry Weissman # Dan Mosedale use diagnostics; use strict; require "CGI.pl"; use Date::Parse; # Shut up misguided -w warnings about "used only once". "use vars" just # doesn't work for me. sub sillyness { my $zz; $zz = $::defaultqueryname; $zz = $::unconfirmedstate; $zz = @::components; $zz = @::default_column_list; $zz = @::keywordsbyname; $zz = @::legal_keywords; $zz = @::legal_platform; $zz = @::legal_priority; $zz = @::legal_product; $zz = @::legal_resolution_no_dup; $zz = @::legal_severity; $zz = @::versions; $zz = @::target_milestone; }; my $serverpush = 0; ConnectToDatabase(); #print "Content-type: text/plain\n\n"; # Handy for debugging. #$::FORM{'debug'} = 1; if (!defined $::FORM{'cmdtype'}) { # This can happen if there's an old bookmark to a query... $::FORM{'cmdtype'} = 'doit'; } sub SqlifyDate { my ($str) = (@_); if (!defined $str) { $str = ""; } my $date = str2time($str); if (!defined $date) { PuntTryAgain("The string '$str' is not a legal date."); } return time2str("%Y/%m/%d %H:%M:%S", $date); } sub GetByWordList { my ($field, $strs) = (@_); my @list; foreach my $w (split(/[\s,]+/, $strs)) { my $word = $w; if ($word ne "") { $word =~ tr/A-Z/a-z/; $word = SqlQuote(quotemeta($word)); $word =~ s/^'//; $word =~ s/'$//; $word = '(^|[^a-z0-9])' . $word . '($|[^a-z0-9])'; push(@list, "lower($field) regexp '$word'"); } } return \@list; } sub Error { my ($str) = (@_); if (!$serverpush) { print "Content-type: text/html\n\n"; } PuntTryAgain($str); } sub GenerateSQL { my $debug = 0; my ($fieldsref, $supptablesref, $wherepartref, $urlstr) = (@_); my @fields; my @supptables; my @wherepart; @fields = @$fieldsref if $fieldsref; @supptables = @$supptablesref if $supptablesref; @wherepart = @$wherepartref if $wherepartref; my %F; my %M; ParseUrlString($urlstr, \%F, \%M); my @specialchart; my @andlist; # First, deal with all the old hard-coded non-chart-based poop. unshift(@supptables, ("profiles map_assigned_to", "profiles map_reporter", "LEFT JOIN profiles map_qa_contact ON bugs.qa_contact = map_qa_contact.userid")); unshift(@wherepart, ("bugs.assigned_to = map_assigned_to.userid", "bugs.reporter = map_reporter.userid", "bugs.groupset & $::usergroupset = bugs.groupset")); my $minvotes; if (defined $F{'votes'}) { my $c = trim($F{'votes'}); if ($c ne "") { if ($c !~ /^[0-9]*$/) { return Error("The 'At least ___ votes' field must be a\n" . "simple number. You entered \"$c\", which\n" . "doesn't cut it."); } push(@specialchart, ["votes", "greaterthan", $c - 1]); } } if ($M{'bug_id'}) { my $type = "anyexact"; if ($F{'bugidtype'} && $F{'bugidtype'} eq 'exclude') { $type = "noexact"; } push(@specialchart, ["bug_id", $type, join(',', @{$M{'bug_id'}})]); } if (defined $F{'sql'}) { die "Invalid sql: $F{'sql'}" if $F{'sql'} =~ /;/; push(@wherepart, "( $F{'sql'} )"); } my @legal_fields = ("product", "version", "rep_platform", "op_sys", "bug_status", "resolution", "priority", "bug_severity", "assigned_to", "reporter", "component", "target_milestone", "groupset"); foreach my $field (keys %F) { if (lsearch(\@legal_fields, $field) != -1) { push(@specialchart, [$field, "anyexact", join(',', @{$M{$field}})]); } } if ($F{'keywords'}) { my $t = $F{'keywords_type'}; if (!$t || $t eq "or") { $t = "anywords"; } push(@specialchart, ["keywords", $t, $F{'keywords'}]); } foreach my $id ("1", "2") { if (!defined ($F{"email$id"})) { next; } my $email = trim($F{"email$id"}); if ($email eq "") { next; } my $type = $F{"emailtype$id"}; if ($type eq "exact") { $type = "anyexact"; foreach my $name (split(',', $email)) { $name = trim($name); if ($name) { DBNameToIdAndCheck($name); } } } my @clist; foreach my $field ("assigned_to", "reporter", "cc", "qa_contact") { if ($F{"email$field$id"}) { push(@clist, $field, $type, $email); } } if ($F{"emaillongdesc$id"}) { my $table = "longdescs_"; push(@supptables, "longdescs $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); my $ptable = "longdescnames_"; push(@supptables, "LEFT JOIN profiles $ptable ON $table.who = $ptable.userid"); push(@clist, "$ptable.login_name", $type, $email); } if (@clist) { push(@specialchart, \@clist); } else { return Error("You must specify one or more fields in which to\n" . "search for $email.\n"); } } if (defined $F{'changedin'}) { my $c = trim($F{'changedin'}); if ($c ne "") { if ($c !~ /^[0-9]*$/) { return Error("The 'changed in last ___ days' field must be\n" . "a simple number. You entered \"$c\", which\n" . "doesn't cut it."); } push(@specialchart, ["changedin", "lessthan", $c + 1]); } } my $ref = $M{'chfield'}; if (defined $ref) { my $which = lsearch($ref, "[Bug creation]"); if ($which >= 0) { splice(@$ref, $which, 1); push(@specialchart, ["creation_ts", "greaterthan", SqlifyDate($F{'chfieldfrom'})]); my $to = $F{'chfieldto'}; if (defined $to) { $to = trim($to); if ($to ne "" && $to !~ /^now$/i) { push(@specialchart, ["creation_ts", "lessthan", SqlifyDate($to)]); } } } } if (defined $ref && 0 < @$ref) { push(@supptables, "bugs_activity actcheck"); my @list; foreach my $f (@$ref) { push(@list, "\nactcheck.fieldid = " . GetFieldID($f)); } push(@wherepart, "actcheck.bug_id = bugs.bug_id"); push(@wherepart, "(" . join(' OR ', @list) . ")"); push(@wherepart, "actcheck.bug_when >= " . SqlQuote(SqlifyDate($F{'chfieldfrom'}))); my $to = $F{'chfieldto'}; if (defined $to) { $to = trim($to); if ($to ne "" && $to !~ /^now$/i) { push(@wherepart, "actcheck.bug_when <= " . SqlQuote(SqlifyDate($to))); } } my $value = $F{'chfieldvalue'}; if (defined $value) { $value = trim($value); if ($value ne "") { push(@wherepart, "actcheck.newvalue = " . SqlQuote($value)) } } } foreach my $f ("short_desc", "long_desc", "bug_file_loc", "status_whiteboard") { if (defined $F{$f}) { my $s = trim($F{$f}); if ($s ne "") { my $n = $f; my $q = SqlQuote($s); my $type = $F{$f . "_type"}; push(@specialchart, [$f, $type, $s]); } } } my $chartid; my $f; my $ff; my $t; my $q; my $v; my $term; my %funcsbykey; my @funcdefs = ( "^(assigned_to|reporter)," => sub { push(@supptables, "profiles map_$f"); push(@wherepart, "bugs.$f = map_$f.userid"); $f = "map_$f.login_name"; }, "^qa_contact," => sub { push(@supptables, "LEFT JOIN profiles map_qa_contact ON bugs.qa_contact = map_qa_contact.userid"); $f = "map_$f.login_name"; }, "^cc," => sub { push(@supptables, ("LEFT JOIN cc cc_$chartid ON bugs.bug_id = cc_$chartid.bug_id LEFT JOIN profiles map_cc_$chartid ON cc_$chartid.who = map_cc_$chartid.userid")); $f = "map_cc_$chartid.login_name"; }, "^long_?desc,changedby" => sub { my $table = "longdescs_$chartid"; push(@supptables, "longdescs $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); my $id = DBNameToIdAndCheck($v); $term = "$table.who = $id"; }, "^long_?desc,changedbefore" => sub { my $table = "longdescs_$chartid"; push(@supptables, "longdescs $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); $term = "$table.bug_when < " . SqlQuote(SqlifyDate($v)); }, "^long_?desc,changedafter" => sub { my $table = "longdescs_$chartid"; push(@supptables, "longdescs $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); $term = "$table.bug_when > " . SqlQuote(SqlifyDate($v)); }, "^long_?desc," => sub { my $table = "longdescs_$chartid"; push(@supptables, "longdescs $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); $f = "$table.thetext"; }, "^attachments\..*," => sub { my $table = "attachments_$chartid"; push(@supptables, "LEFT JOIN attachments $table ON bugs.bug_id = $table.bug_id"); $f =~ m/^attachments\.(.*)$/; my $field = $1; if ($t eq "changedby") { $v = DBNameToIdAndCheck($v); $q = SqlQuote($v); $field = "submitter_id"; $t = "equals"; } elsif ($t eq "changedbefore") { $v = SqlifyDate($v); $q = SqlQuote($v); $field = "creation_ts"; $t = "lessthan"; } elsif ($t eq "changedafter") { $v = SqlifyDate($v); $q = SqlQuote($v); $field = "creation_ts"; $t = "greaterthan"; } if ($field eq "ispatch") { if ($v ne "0" && $v ne "1") { return Error("The only legal values for the 'Attachment is patch' field is 0 or 1."); } } $f = "$table.$field"; }, "^changedin," => sub { $f = "(to_days(now()) - to_days(bugs.delta_ts))"; }, "^keywords," => sub { GetVersionTable(); my @list; my $table = "keywords_$chartid"; foreach my $value (split(/[\s,]+/, $v)) { if ($value eq '') { next; } my $id = $::keywordsbyname{$value}; if ($id) { push(@list, "$table.keywordid = $id"); } else { return Error("Unknown keyword named $v.\n" . "

The legal keyword names are\n" . "" . "listed here.\n"); } } my $haveawordterm; if (@list) { $haveawordterm = "(" . join(' OR ', @list) . ")"; if ($t eq "anywords") { $term = $haveawordterm; } elsif ($t eq "allwords") { $ref = $funcsbykey{",$t"}; &$ref; if ($term && $haveawordterm) { $term = "(($term) AND $haveawordterm)"; } } } if ($term) { push(@supptables, "keywords $table"); push(@wherepart, "$table.bug_id = bugs.bug_id"); } }, "^(dependson|blocked)," => sub { push(@supptables, "dependencies"); $ff = "dependencies.$f"; $ref = $funcsbykey{",$t"}; &$ref; push(@wherepart, "$term"); }, ",equals" => sub { $term = "$ff = $q"; }, ",notequals" => sub { $term = "$ff != $q"; }, ",casesubstring" => sub { $term = "INSTR($ff, $q)"; }, ",(substring|substr)" => sub { $term = "INSTR(LOWER($ff), " . lc($q) . ")"; }, ",notsubstring" => sub { $term = "INSTR(LOWER($ff), " . lc($q) . ") = 0"; }, ",regexp" => sub { $term = "LOWER($ff) REGEXP $q"; }, ",notregexp" => sub { $term = "LOWER($ff) NOT REGEXP $q"; }, ",lessthan" => sub { $term = "$ff < $q"; }, ",greaterthan" => sub { $term = "$ff > $q"; }, ",anyexact" => sub { my @list; foreach my $w (split(/,/, $v)) { if ($w eq "---" && $f !~ /milestone/) { $w = ""; } push(@list, "$ff = " . SqlQuote($w)); } $term = join(" OR ", @list); }, ",anywords" => sub { $term = join(" OR ", @{GetByWordList($ff, $v)}); }, ",allwords" => sub { $term = join(" AND ", @{GetByWordList($ff, $v)}); }, ",nowords" => sub { my @list = @{GetByWordList($ff, $v)}; if (@list) { $term = "NOT (" . join(" OR ", @list) . ")"; } }, ",changedbefore" => sub { my $table = "act_$chartid"; my $ftable = "fielddefs_$chartid"; push(@supptables, "bugs_activity $table"); push(@supptables, "fielddefs $ftable"); push(@wherepart, "$table.bug_id = bugs.bug_id"); push(@wherepart, "$table.fieldid = $ftable.fieldid"); $term = "($ftable.name = '$f' AND $table.bug_when < $q)"; }, ",changedafter" => sub { my $table = "act_$chartid"; my $ftable = "fielddefs_$chartid"; push(@supptables, "bugs_activity $table"); push(@supptables, "fielddefs $ftable"); push(@wherepart, "$table.bug_id = bugs.bug_id"); push(@wherepart, "$table.fieldid = $ftable.fieldid"); $term = "($ftable.name = '$f' AND $table.bug_when > $q)"; }, ",changedto" => sub { my $table = "act_$chartid"; my $ftable = "fielddefs_$chartid"; push(@supptables, "bugs_activity $table"); push(@supptables, "fielddefs $ftable"); push(@wherepart, "$table.bug_id = bugs.bug_id"); push(@wherepart, "$table.fieldid = $ftable.fieldid"); $term = "($ftable.name = '$f' AND $table.newvalue = $q)"; }, ",changedby" => sub { my $table = "act_$chartid"; my $ftable = "fielddefs_$chartid"; push(@supptables, "bugs_activity $table"); push(@supptables, "fielddefs $ftable"); push(@wherepart, "$table.bug_id = bugs.bug_id"); push(@wherepart, "$table.fieldid = $ftable.fieldid"); my $id = DBNameToIdAndCheck($v); $term = "($ftable.name = '$f' AND $table.who = $id)"; }, ); my @funcnames; while (@funcdefs) { my $key = shift(@funcdefs); my $value = shift(@funcdefs); if ($key =~ /^[^,]*$/) { die "All defs in %funcs must have a comma in their name: $key"; } if (exists $funcsbykey{$key}) { die "Duplicate key in %funcs: $key"; } $funcsbykey{$key} = $value; push(@funcnames, $key); } my $chart = -1; my $row = 0; foreach my $ref (@specialchart) { my $col = 0; while (@$ref) { $F{"field$chart-$row-$col"} = shift(@$ref); $F{"type$chart-$row-$col"} = shift(@$ref); $F{"value$chart-$row-$col"} = shift(@$ref); if ($debug) { print qq{

$F{"field$chart-$row-$col"} | $F{"type$chart-$row-$col"} | $F{"value$chart-$row-$col"}*\n}; } $col++; } $row++; } for ($chart=-1 ; $chart < 0 || exists $F{"field$chart-0-0"} ; $chart++) { $chartid = $chart >= 0 ? $chart : ""; for (my $row = 0 ; exists $F{"field$chart-$row-0"} ; $row++) { my @orlist; for (my $col = 0 ; exists $F{"field$chart-$row-$col"} ; $col++) { $f = $F{"field$chart-$row-$col"} || "noop"; $t = $F{"type$chart-$row-$col"} || "noop"; $v = $F{"value$chart-$row-$col"}; $v = "" if !defined $v; $v = trim($v); if ($f eq "noop" || $t eq "noop" || $v eq "") { next; } $q = SqlQuote($v); my $func; $term = undef; foreach my $key (@funcnames) { if ("$f,$t" =~ m/$key/) { my $ref = $funcsbykey{$key}; if ($debug) { print "

$key ($f , $t ) => "; } $ff = $f; if ($f !~ /\./) { $ff = "bugs.$f"; } &$ref; if ($debug) { print "$f , $t , $term"; } if ($term) { last; } } } if ($term) { push(@orlist, $term); } else { my $errstr = "Can't seem to handle " . qq{'$F{"field$chart-$row-$col"}' and } . qq{'$F{"type$chart-$row-$col"}' } . "together"; die "Internal error: $errstr" if $chart < 0; return Error($errstr); } } if (@orlist) { push(@andlist, "(" . join(" OR ", @orlist) . ")"); } } } my %suppseen = ("bugs" => 1); my $suppstring = "bugs"; foreach my $str (@supptables) { if (!$suppseen{$str}) { if ($str !~ /^LEFT JOIN/i) { $suppstring .= ","; } $suppstring .= " $str"; $suppseen{$str} = 1; } } my $query = ("SELECT " . join(', ', @fields) . " FROM $suppstring" . " WHERE " . join(' AND ', (@wherepart, @andlist)) . " GROUP BY bugs.bug_id"); if ($debug) { print "

" . value_quote($query) . "

\n"; exit(); } return $query; } sub LookupNamedQuery { my ($name) = (@_); confirm_login(); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); SendSQL("SELECT query FROM namedqueries " . "WHERE userid = $userid AND name = " . SqlQuote($name)); my $result = FetchOneColumn(); if (!defined $result) { print "Content-type: text/html\n\n"; PutHeader("Something weird happened"); print qq{The named query $name seems to no longer exist.}; PutFooter(); exit; } return $result; } CMD: for ($::FORM{'cmdtype'}) { /^runnamed$/ && do { $::buffer = LookupNamedQuery($::FORM{"namedcmd"}); ProcessFormFields($::buffer); last CMD; }; /^editnamed$/ && do { my $url = "query.cgi?" . LookupNamedQuery($::FORM{"namedcmd"}); print qq{Content-type: text/html Refresh: 0; URL=$url What a hack. Loading your query named $::FORM{'namedcmd'}... }; exit; }; /^forgetnamed$/ && do { confirm_login(); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); SendSQL("DELETE FROM namedqueries WHERE userid = $userid " . "AND name = " . SqlQuote($::FORM{'namedcmd'})); print "Content-type: text/html\n\n"; PutHeader("Forget what?", ""); print qq{ OK, the $::FORM{'namedcmd'} query is gone.

Go back to the query page. }; PutFooter(); exit; }; /^asdefault$/ && do { confirm_login(); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); print "Content-type: text/html\n\n"; SendSQL("REPLACE INTO namedqueries (userid, name, query) VALUES " . "($userid, '$::defaultqueryname'," . SqlQuote($::buffer) . ")"); PutHeader("OK, default is set"); print qq{ OK, you now have a new default query. You may also bookmark the result of any individual query.

Go back to the query page, using the new default. }; PutFooter(); exit(); }; /^asnamed$/ && do { confirm_login(); my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"}); print "Content-type: text/html\n\n"; my $name = trim($::FORM{'newqueryname'}); if ($name eq "" || $name =~ /[<>&]/) { PutHeader("Please pick a valid name for your new query"); print "Click the Back button and type in a valid name\n"; print "for this query. (Query names should not contain unusual\n"; print "characters.)\n"; PutFooter(); exit(); } $::buffer =~ s/[\&\?]cmdtype=[a-z]+//; my $qname = SqlQuote($name); SendSQL("SELECT query FROM namedqueries " . "WHERE userid = $userid AND name = $qname"); if (!FetchOneColumn()) { SendSQL("REPLACE INTO namedqueries (userid, name, query) " . "VALUES ($userid, $qname, " . SqlQuote($::buffer) . ")"); } else { SendSQL("UPDATE namedqueries SET query = " . SqlQuote($::buffer) . " WHERE userid = $userid AND name = $qname"); } PutHeader("OK, query saved."); print qq{ OK, you have a new query named $name


Go back to the query page }; PutFooter(); exit; }; } sub DefCol { my ($name, $k, $t, $s, $q) = (@_); $::key{$name} = $k; $::title{$name} = $t; if (defined $s && $s ne "") { $::sortkey{$name} = $s; } if (!defined $q || $q eq "") { $q = 0; } $::needquote{$name} = $q; } DefCol("opendate", "date_format(bugs.creation_ts,'%Y-%m-%d')", "Opened", "bugs.creation_ts"); DefCol("changeddate", "date_format(bugs.delta_ts,'%Y-%m-%d')", "Changed", "bugs.delta_ts"); DefCol("severity", "bugs.bug_severity", "Sev", "bugs.bug_severity"); DefCol("priority", "bugs.priority", "Pri", "bugs.priority"); DefCol("platform", "bugs.rep_platform", "Plt", "bugs.rep_platform"); DefCol("owner", "map_assigned_to.login_name", "Owner", "map_assigned_to.login_name"); DefCol("reporter", "map_reporter.login_name", "Reporter", "map_reporter.login_name"); DefCol("qa_contact", "map_qa_contact.login_name", "QAContact", "map_qa_contact.login_name"); DefCol("status", "bugs.bug_status", "State", "bugs.bug_status"); DefCol("resolution", "bugs.resolution", "Result", "bugs.resolution"); DefCol("summary", "substring(bugs.short_desc, 1, 60)", "Summary", "", 1); DefCol("summaryfull", "bugs.short_desc", "Summary", "", 1); DefCol("status_whiteboard", "bugs.status_whiteboard", "StatusSummary", "bugs.status_whiteboard", 1); DefCol("component", "bugs.component", "Comp", "bugs.component"); DefCol("product", "bugs.product", "Product", "bugs.product"); DefCol("version", "bugs.version", "Vers", "bugs.version"); DefCol("os", "bugs.op_sys", "OS", "bugs.op_sys"); DefCol("target_milestone", "bugs.target_milestone", "TargetM", "bugs.target_milestone"); DefCol("votes", "bugs.votes", "Votes", "bugs.votes desc"); DefCol("keywords", "bugs.keywords", "Keywords", "bugs.keywords"); my @collist; @collist = ($::vertical, $::horizontal); my $minvotes; if (defined $::FORM{'votes'}) { if (trim($::FORM{'votes'}) ne "") { if (! (grep {/^votes$/} @collist)) { push(@collist, 'votes'); } } } my @fields = ("bugs.bug_id", "bugs.groupset"); foreach my $c (@collist) { if (exists $::needquote{$c}) { push(@fields, "$::key{$c}"); } } if ($::FORM{'regetlastlist'}) { if (!$::COOKIE{'BUGLIST'}) { print qq{ Sorry, I seem to have lost the cookie that recorded the results of your last query. You will have to start over at the query page. }; PutFooter(); exit; } my @list = split(/:/, $::COOKIE{'BUGLIST'}); $::FORM{'bug_id'} = join(',', @list); if (!$::FORM{'order'}) { $::FORM{'order'} = 'reuse last sort'; } $::buffer = "bug_id=" . $::FORM{'bug_id'} . "&order=" . url_quote($::FORM{'order'}); } ReconnectToShadowDatabase(); my $query = GenerateSQL(\@fields, undef, undef, $::buffer); if ($::COOKIE{'LASTORDER'}) { if ((!$::FORM{'order'}) || $::FORM{'order'} =~ /^reuse/i) { $::FORM{'order'} = url_decode($::COOKIE{'LASTORDER'}); } } if (defined $::FORM{'order'} && $::FORM{'order'} ne "") { $query .= " ORDER BY "; $::FORM{'order'} =~ s/votesum/bugs.votes/; # Silly backwards compatability # hack. $::FORM{'order'} =~ s/assign\.login_name/map_assigned_to.login_name/g; # Another backwards compatability hack. ORDER: for ($::FORM{'order'}) { /\./ && do { # This (hopefully) already has fieldnames in it, so we're done. last ORDER; }; /Number/ && do { $::FORM{'order'} = "bugs.bug_id"; last ORDER; }; /Import/ && do { $::FORM{'order'} = "bugs.priority, bugs.bug_severity"; last ORDER; }; /Assign/ && do { $::FORM{'order'} = "map_assigned_to.login_name, bugs.bug_status, priority, bugs.bug_id"; last ORDER; }; # DEFAULT $::FORM{'order'} = "bugs.bug_status, bugs.priority, map_assigned_to.login_name, bugs.bug_id"; } die "Invalid order: $::FORM{'order'}" unless $::FORM{'order'} =~ /^([a-zA-Z0-9_., ]+)$/; # Extra special disgusting hack: if we are ordering by target_milestone, # change it to order by the sortkey of the target_milestone first. my $order = $::FORM{'order'}; if ($order =~ /bugs.target_milestone/) { $query =~ s/ WHERE / LEFT JOIN milestones ms_order ON ms_order.value = bugs.target_milestone AND ms_order.product = bugs.product WHERE /; $order =~ s/bugs.target_milestone/ms_order.sortkey,ms_order.value/; } $query .= $order; } if ($::FORM{'debug'} && $serverpush) { print "

" . value_quote($query) . "

\n"; } if (Param('expectbigqueries')) { SendSQL("set option SQL_BIG_TABLES=1"); } SendSQL($query); my $fields = $::buffer; $fields =~ s/[&?]order=[^&]*//g; $fields =~ s/[&?]cmdtype=[^&]*//g; my $orderpart; my $oldorder; if (defined $::FORM{'order'} && trim($::FORM{'order'}) ne "") { $orderpart = "&order=" . url_quote("$::FORM{'order'}"); $oldorder = url_quote(", $::FORM{'order'}"); } else { $orderpart = ""; $oldorder = ""; } my @th; foreach my $c (@collist) { if (exists $::needquote{$c}) { my $h = ""; if ($::needquote{$c}) { $h .= ""; } else { $h .= ""; } if (defined $::sortkey{$c}) { $h .= "$::title{$c}"; } else { $h .= $::title{$c}; } $h .= ""; push(@th, $h); } } my $tablestart = "
ID"; my $splitheader = 0; if ($::COOKIE{'SPLITHEADER'}) { $splitheader = 1; } if ($splitheader) { $tablestart =~ s/got col=$col row=$row"); $row = "unresolved" if ($row eq "" and $::vertical eq "resolution"); $row = "(empty)" if $row eq ""; $col = "unresolved" if ($col eq "" and $::horizontal eq "resolution"); $col = "(empty)" if $col eq ""; if(defined($row_stuff{$row})) { $row_stuff{$row}{$col} ++; } else { $row_stuff{$row}{$col} = 1; }; $col_names{$col}++; $row_names{$row}++; } # determine the values of the axes my @verticals = sort(keys(%row_stuff)); my @horizontals = sort(keys(%col_names)); # install totals for my $col (@horizontals) { $row_stuff{"totals"}{$col} = $col_names{$col}; } for my $row (@verticals) { $row_stuff{$row}{"totals"} = $row_names{$row}; } $row_stuff{"totals"}{"totals"} = $count; push(@horizontals,"totals"); push(@verticals,"totals"); # for titles unshift(@horizontals," "); unshift(@verticals," "); # description my $descript = $::buffer; $descript =~ s/[^&]*=&//g; # remove parameters set to nothing $descript =~ s/[^&]*=substring&//g; # remove anything "=subsstring" for my $param ("emailassigned_to1", "emailreporter2", "emailtype1", "emailtype2", "field0-0-0", "type0-0-0", "cmdtype", "bugidtype", "order", "keywords_type") { $descript =~ s/&${param}=[^&]*//g; # remove parameters set to nothing }; $descript =~ s/chfieldto=[^&]*&?//g if ! ($descript =~ /chfieldlfrom=/); $descript =~ s/csv=[^&]&//g; # remove csv= (first param) $descript =~ s/\&/\n/g; # convert "&" to newlines if($::FORM{"csv"}) { # if comma separated variable format for(my $row=0;defined($verticals[$row]);$row++) { for(my $col=0;defined($horizontals[$col]);$col++) { if($row == 0) { my $show = $horizontals[$col]; $show =~ s/@.*$//; print("$show,"); } elsif ($col == 0) { my $show = $verticals[$row]; $show =~ s/@.*$//; print("$show,"); } else { my $count = $row_stuff{$verticals[$row]}{$horizontals[$col]}; if(defined($count) && $count > 0) { print("$count,"); } else { print("0,"); }; }; }; print("\n"); }; print("\n" . time2str("%a %b %e %T %Z %Y", time()) . "\n\n$descript"); return 1; }; # start printing the chart for the HTML (non-CSV) case print "
\n"; print "
" . time2str("%a %b %e %T %Z %Y", time()) . ""; if (defined $::FORM{'debug'}) { print "

" . value_quote($query) . "

\n"; } sub ConstrainBuglist { my($link, $name, $value) = @_; return $link if $value eq "totals"; my $param_name = $::key{$name}; $param_name =~ s/^[^.]*\.//; # delete stuff before the period (e.g. bugs.severity -> severity) if($name eq "owner") { $param_name = "email1"; $link =~ s/emailassigned_to1=[^&]*&?//g; # delete any other instance $link .= "&emailassigned_to1=1"; $link =~ s/emailtype1=[^&]*&?//g; # delete any other instance $link .= "&emailtype1=exact"; }; if($name eq "reporter") { $param_name = "email2"; $link =~ s/emailreporter2=[^&]*&?//g; # delete any other instance $link .= "&emailreporter2=1"; $link =~ s/emailtype2=[^&]*&?//g; # delete any other instance $link .= "&emailtype2=exact"; }; $link =~ s/$param_name=[^&]*&?//g; # delete any other instance $link .= "&$param_name=$value"; # append value return $link; } sub DeriveBuglistLink { my($curr_vert, $curr_horiz) = @_; my $link = $::buffer; $link = ConstrainBuglist($link,$::vertical,$curr_vert); $link = ConstrainBuglist($link,$::horizontal,$curr_horiz); $link = "buglist.cgi?" . $link; return $link; } # start the table which labels our axis categories print("
$::horizontal
$::vertical"); # print the table of numbers and headers print(""); my @colors = ("ffffff","dfefff", "dddddd","c3d3ed"); for(my $row=0;defined($verticals[$row]);$row++) { print(" "); my $curr_vert = $verticals[$row]; for(my $col=0;defined($horizontals[$col]);$col++) { my $curr_horiz = $horizontals[$col]; my $color = $colors[2 * ($row & 1) + ($col & 1)]; $color = "ffeeee" if $curr_horiz eq "totals"; $color = "ffeeee" if $curr_vert eq "totals"; print(""); }; print("\n"); }; # close tables print("
"); if($row == 0) { my $show = $curr_horiz; $show =~ s/@.*$//; print($show); } elsif ($col == 0) { my $show = $curr_vert; $show =~ s/@.*$//; print($show); } else { my $count = $row_stuff{$curr_vert}{$curr_horiz}; if(defined($count) && $count > 0) { print("$count\n"); } else { print("."); }; }; print("
\n"); print("
\n"); print "

\n"; # give some warnings if(($::vertical eq "resolution" or $::horizontal eq "resolution") and $::buffer =~ /status=[^&]&?/ && ! ($::buffer =~ /status=RESOLVED/ or $::buffer =~ /status=VERIFIED/ or $::buffer =~ /status=CLOSED/)) { print("
\"Resolution\" is pretty borring when you specify " . "bugs not of RESOLVED, VERIFIED, or CLOSED, huh?"); }; if($::buffer =~ /resolution=[^&]+&?/ and $::buffer =~ /status=[^&]&?/ and ! ($::buffer =~ /status=RESOLVED/ or $::buffer =~ /status=VERIFIED/ or $::buffer =~ /status=CLOSED/)) { print("
Did you really mean to specify a " . "resolution but not bugs of RESOLVED, VERIFIED, or CLOSED?"); }; #print "
\n"; return 1; # needed for successful "do" statement which called us