]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Massive stomp on the query page and buglist page. Added the ability
authorterry%mozilla.org <>
Fri, 28 Jan 2000 09:01:36 +0000 (09:01 +0000)
committerterry%mozilla.org <>
Fri, 28 Jan 2000 09:01:36 +0000 (09:01 +0000)
to use the "boolean charts" to do very powerful queries.

CGI.pl
booleanchart.html [new file with mode: 0644]
buglist.cgi
checksetup.pl
query.cgi

diff --git a/CGI.pl b/CGI.pl
index 770a8d98bd70cbe5f0d079e178e956a833a0ead0..a5c8c73213e79f403ad1cd83199b025e1e4c7f63 100644 (file)
--- a/CGI.pl
+++ b/CGI.pl
@@ -78,10 +78,10 @@ sub url_quote {
 }
 
 
-sub ProcessFormFields {
-    my ($buffer) = (@_);
-    undef %::FORM;
-    undef %::MFORM;
+sub ParseUrlString {
+    my ($buffer, $f, $m) = (@_);
+    undef %$f;
+    undef %$m;
 
     my %isnull;
     my $remaining = $buffer;
@@ -105,13 +105,13 @@ sub ProcessFormFields {
            $value = "";
        }
        if ($value ne "") {
-           if (defined $::FORM{$name}) {
-               $::FORM{$name} .= $value;
-               my $ref = $::MFORM{$name};
+           if (defined $f->{$name}) {
+               $f->{$name} .= $value;
+               my $ref = $m->{$name};
                push @$ref, $value;
            } else {
-               $::FORM{$name} = $value;
-               $::MFORM{$name} = [$value];
+               $f->{$name} = $value;
+               $m->{$name} = [$value];
            }
         } else {
             $isnull{$name} = 1;
@@ -119,15 +119,21 @@ sub ProcessFormFields {
     }
     if (defined %isnull) {
         foreach my $name (keys(%isnull)) {
-            if (!defined $::FORM{$name}) {
-                $::FORM{$name} = "";
-                $::MFORM{$name} = [];
+            if (!defined $f->{$name}) {
+                $f->{$name} = "";
+                $m->{$name} = [];
             }
         }
     }
 }
 
 
+sub ProcessFormFields {
+    my ($buffer) = (@_);
+    return ParseUrlString($buffer, \%::FORM, \%::MFORM);
+}
+
+
 sub ProcessMultipartFormFields {
     my ($boundary) = (@_);
     $boundary =~ s/^-*//;
diff --git a/booleanchart.html b/booleanchart.html
new file mode 100644 (file)
index 0000000..5834c80
--- /dev/null
@@ -0,0 +1,79 @@
+<html> <head>
+<title>The "boolean chart" section of the query page</title>
+</head>
+
+<body>
+<h1>The "boolean chart" section of the query page</h1>
+
+("Boolean chart" is a terrible term; anyone got a better one I can use
+instead?)
+
+<p>
+
+The Bugzilla query page is designed to be reasonably easy to use.
+But, with such ease of use always comes some lack of power.  The
+"boolean chart" section is designed to let you do very powerful
+queries, but it's not the easiest thing to learn (or explain).
+<p>
+So.
+<p>
+
+The boolean chart starts with a single "term".  A term is a
+combination of two pulldown menus and a text field.
+You choose items from the menus, specifying "what kind of thing
+am I searching for" and "what kind of matching do I want", and type in
+a value on the text field, specifying "what should it match".
+
+<p>
+
+The real fun starts when you click on the "Or" or "And" buttons.  If
+you bonk on the "Or" button, then you get a second term to the right
+of the first one.  You can then configure that term, and the result of
+the query will be anything that matches either of the terms.
+
+<p>
+
+Or, you can bonk the "And" button, and get a new term below the
+original one, and now the result of the query will be anything that
+matches both of the terms.
+
+<p>
+
+And you can keep clicking "And" and "Or", and get a page with tons of
+terms.  "Or" has higher precedence than "And".  (In other words, you
+can think of each line of "Or" stuff as having parenthesis around it.)
+
+<p>
+
+The most subtle thing is this "Add another boolean chart" button.
+This is almost the same thing as the "And" button.  The difference is
+if you use one of the fields where several items can be associated
+with a single bug.  This includes "Comments", "CC", and all the
+"changed [something]" entries.  Now, if you have multiple terms that
+all talk about one of these fields, it's ambiguous whether they are
+allowed to be talking about different instances of that field.  So,
+to let you have it both ways, they always mean the same instance,
+unless the terms appear on different charts.
+
+<p>
+
+For example: if you search for "priority changed to P5" and
+"priority changed by person@addr", it will only find bugs where the
+given person at some time changed the priority to P5.  However, if
+what you really want is to find all bugs where the milestone was
+changed at some time by the person, and someone (possibly someone
+else) at some time changed the milestone to P5, then you would put
+the two terms in two different charts.
+
+<p>
+
+Clear as mud?  Please, I beg you, rewrite this document to make
+everything crystal clear, and send the improved version to <a
+href="terry@mozilla.org">Terry</a>.
+
+<hr>
+
+<!-- hhmts start -->
+Last modified: Thu Jan 27 16:56:11 2000
+<!-- hhmts end -->
+</body> </html>
index 5cae83b872ecd6f294183d6cf722eaec391e099d..902d09f12285e8251f7729632760eaf3a07caed2 100755 (executable)
@@ -46,17 +46,565 @@ sub sillyness {
     $zz = @::versions;
 };
 
-
+my $serverpush = 0;
 
 ConnectToDatabase();
 
 # print "Content-type: text/plain\n\n";    # Handy for debugging.
+# $::FORM{'debug'} = 1;
+
+
+if (grep(/^cmd-/, keys(%::FORM))) {
+    my $url = "query.cgi#chart?$::buffer";
+    print qq{Refresh: 0; URL=$url
+Content-type: text/html
+
+<A HREF="$url">Adding field to query page...</A>
+};
+    exit();
+}
+
+
 
 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) {
+        print "\n\n<P>The string '<tt>$str</tt>' is not a legal date.\n";
+        print "<P>Please click the <B>Back</B> button and try again.\n";
+        PutFooter();
+        exit;
+    }
+    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";
+    }
+    print $str;
+    print "\n<P>Please press <B>Back</B> and try again.\n";
+    PutFooter();
+    exit();
+}
+    
+        
+
+
+
+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'}) {
+        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 <tt>$email</tt>.\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 $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.who < " . 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.who > " . 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";
+         },
+         "^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 <code>$v</code>.\n" .
+                                  "<P>The legal keyword names are\n" .
+                                  "<A HREF=describekeywords.cgi>" . 
+                                  "listed here</A>.\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");
+             }
+         },
+
+
+         ",equals" => sub {
+             $term = "$f = $q";
+         },
+         ",notequals" => sub {
+             $term = "$f != $q";
+         },
+         ",casesubstring" => sub {
+             $term = "INSTR($f, $q)";
+         },
+         ",(substring|substr)" => sub {
+             $term = "INSTR(LOWER($f), " . lc($q) . ")";
+         },
+         ",notsubstring" => sub {
+             $term = "INSTR(LOWER($f), " . lc($q) . ") = 0";
+         },
+         ",regexp" => sub {
+             $term = "LOWER($f) REGEXP $q";
+         },
+         ",notregexp" => sub {
+             $term = "LOWER($f) NOT REGEXP $q";
+         },
+         ",lessthan" => sub {
+             $term = "$f < $q";
+         },
+         ",greaterthan" => sub {
+             $term = "$f > $q";
+         },
+         ",anyexact" => sub {
+             my @list;
+             foreach my $w (split(/,/, $v)) {
+                 if ($w eq "---") {
+                     $w = "";
+                 }
+                 push(@list, "$f = " . SqlQuote($w));
+             }
+             $term = join(" OR ", @list);
+         },
+         ",anywords" => sub {
+             $term = join(" OR ", @{GetByWordList($f, $v)});
+         },
+         ",allwords" => sub {
+             $term = join(" AND ", @{GetByWordList($f, $v)});
+         },
+         ",nowords" => sub {
+             my @list = @{GetByWordList($f, $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{<P>$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 "<P>$key ($f , $t ) => ";
+                        }
+                        &$ref;
+                        if ($debug) {
+                            print "$f , $t , $term";
+                        }
+                        if ($term) {
+                            last;
+                        }
+                    }
+                }
+                if ($term) {
+                    push(@orlist, $term);
+                } else {
+                    my $errstr = "Can't seem to handle " .
+                        qq{'<code>$F{"field$chart-$row-$col"}</code>' and } .
+                            qq{'<code>$F{"type$chart-$row-$col"}</code>' } .
+                                "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 "<P><CODE>" . value_quote($query) . "</CODE><P>\n";
+        exit();
+    }
+    return $query;
+}
+
+
+         
 sub LookupNamedQuery {
     my ($name) = (@_);
     confirm_login();
@@ -164,7 +712,6 @@ OK, you have a new query named <code>$name</code>
 }
 
 
-my $serverpush = 0;
 if ($ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/ && $ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/ ) {
     # Search for real Netscape 3 and up.  http://www.browsercaps.org used as source of
     # browsers compatbile with server-push.  It's a Netscape hack, incompatbile
@@ -205,9 +752,11 @@ DefCol("severity", "substring(bugs.bug_severity, 1, 3)", "Sev",
 DefCol("priority", "substring(bugs.priority, 1, 3)", "Pri", "bugs.priority");
 DefCol("platform", "substring(bugs.rep_platform, 1, 3)", "Plt",
        "bugs.rep_platform");
-DefCol("owner", "assign.login_name", "Owner", "assign.login_name");
-DefCol("reporter", "report.login_name", "Reporter", "report.login_name");
-DefCol("qa_contact", "qacont.login_name", "QAContact", "qacont.login_name");
+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", "substring(bugs.bug_status,1,4)", "State", "bugs.bug_status");
 DefCol("resolution", "substring(bugs.resolution,1,4)", "Result",
        "bugs.resolution");
@@ -233,16 +782,7 @@ if (defined $::COOKIE{'COLUMNLIST'}) {
 
 my $minvotes;
 if (defined $::FORM{'votes'}) {
-    my $c = trim($::FORM{'votes'});
-    if ($c ne "") {
-        if ($c !~ /^[0-9]*$/) {
-            print "\n\n<P>The 'At least ___ votes' field must be a simple ";
-            print "number. You entered \"$c\", which doesn't cut it.";
-            print "<P>Please click the <B>Back</B> button and try again.\n";
-            PutFooter();
-            exit;
-        }
-        $minvotes = $c;
+    if (trim($::FORM{'votes'}) ne "") {
         if (! (grep {/^votes$/} @collist)) {
             push(@collist, 'votes');
         }
@@ -259,36 +799,20 @@ if ($dotweak) {
 }
 
 
-my $query = "select bugs.bug_id, bugs.groupset";
+my @fields = ("bugs.bug_id", "bugs.groupset");
 
 foreach my $c (@collist) {
     if (exists $::needquote{$c}) {
-        $query .= ",
-\t$::key{$c}";
+        push(@fields, "$::key{$c}");
     }
 }
 
 
 if ($dotweak) {
-    $query .= ",
-bugs.product,
-bugs.bug_status";
+    push(@fields, "bugs.product", "bugs.bug_status");
 }
 
 
-$query .= "
-from   bugs,
-       profiles assign,
-       profiles report
-       left join profiles qacont on bugs.qa_contact = qacont.userid,
-       versions projector
-
-where  bugs.assigned_to = assign.userid 
-and    bugs.reporter = report.userid
-and    bugs.product = projector.program
-and    bugs.version = projector.value
-and    bugs.groupset & $::usergroupset = bugs.groupset
-";
 
 if ($::FORM{'regetlastlist'}) {
     if (!$::COOKIE{'BUGLIST'}) {
@@ -309,331 +833,10 @@ query.  You will have to start over at the <A HREF="query.cgi">query page</A>.
         url_quote($::FORM{'order'});
 }
 
-if ((defined $::FORM{'emailcc1'} && $::FORM{'emailcc1'}) ||
-    (defined $::FORM{'emailcc2'} && $::FORM{'emailcc2'})) {
-
-    # We need to poke into the CC table.  Do weird SQL left join stuff so that
-    # we can look in the CC table, but won't reject any bugs that don't have
-    # any CC fields.
-    $query =~ s/bugs,/bugs left join cc on bugs.bug_id = cc.bug_id left join profiles ccname on cc.who = ccname.userid,/;
-}
-
-my $needlongdescs = 0;          # Whether we need to patch in the longdescs
-                                # table.
-
-
-if ($::MFORM{'bug_id'}) {
-    my @list = grep(!/^$/, split(/[^0-9]+/, join(',', @{$::MFORM{'bug_id'}})));
-    if (@list) {
-        my $verb = "IN";
-        if ($::FORM{'bugidtype'} && $::FORM{'bugidtype'} eq 'exclude') {
-            $verb = "NOT IN";
-        }
-        $query .= " AND bugs.bug_id $verb (" . join(',', @list) . ") ";
-    }
-}
-    
-
-
-if (defined $::FORM{'sql'}) {
-  $query .= "and (\n$::FORM{'sql'}\n)"
-} else {
-  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 %::FORM) {
-      my $or = "";
-      if (lsearch(\@legal_fields, $field) != -1 && $::FORM{$field} ne "") {
-          $query .= "\tand (\n";
-          if ($field eq "assigned_to" || $field eq "reporter") {
-              foreach my $p (split(/,/, $::FORM{$field})) {
-                  my $whoid = DBNameToIdAndCheck($p);
-                  $query .= "\t\t${or}bugs.$field = $whoid\n";
-                  $or = "or ";
-              }
-          } else {
-              my $ref = $::MFORM{$field};
-              foreach my $v (@$ref) {
-                  if ($v eq "(empty)") {
-                      $query .= "\t\t${or}bugs.$field is null\n";
-                  } else {
-                      if ($v eq "---") {
-                          $query .= "\t\t${or}bugs.$field = ''\n";
-                      } else {
-                          $query .= "\t\t${or}bugs.$field = " . SqlQuote($v) .
-                              "\n";
-                      }
-                  }
-                  $or = "or ";
-              }
-          }
-          $query .= "\t)\n";
-      }
-  }
-}
-
-if ($::FORM{'keywords'}) {
-    GetVersionTable();
-    my @list;
-    foreach my $v (split(/[\s,]+/, $::FORM{'keywords'})) {
-        if ($v eq '') {
-            next;
-        }
-        my $id = $::keywordsbyname{$v};
-        if ($id) {
-            push(@list, "keywords.keywordid = $id");
-        } else {
-            print "Unknown keyword named <code>$v</code>.\n";
-            print "<P>The legal keyword names are <A HREF=describekeywords.cgi>";
-            print "listed here</A>.\n";
-            print "<P>Please click the <B>Back</B> button and try again.\n";
-            PutFooter();
-            exit;
-        }
-    }
-    if (@list) {
-        $query =~ s/where/, keywords where/;
-        my $type = $::FORM{'keywords_type'};
-        my $notopt = "";
-        if ($type eq "nowords") {
-            # Ought to take advantage of keyword table somehow! ###
-            my $extra = GetByWordList("bugs.keywords", $::FORM{'keywords'},
-                                      "or");
-            $extra =~ s/AND/AND NOT/i;
-            $query .= $extra;
-        } else {
-            $query .= "and keywords.bug_id = bugs.bug_id and $notopt (" .
-                join(" or ", @list) . ")\n";
-            if ($type eq "allwords") {
-                # This needs to be tuned to take better advantage of the
-                # keyword table!
-                $query .= GetByWordList("bugs.keywords", $::FORM{'keywords'},
-                                        "and");
-            }
-        }
-    }
-}
-
-
-foreach my $id ("1", "2") {
-    if (!defined ($::FORM{"email$id"})) {
-        next;
-    }
-    my $email = trim($::FORM{"email$id"});
-    if ($email eq "") {
-        next;
-    }
-    my $qemail = SqlQuote($email); 
-    my $type = $::FORM{"emailtype$id"};
-    my $emailid;
-    if ($type eq "exact") {
-        $emailid = DBNameToIdAndCheck($email);
-    }
-
-    my $foundone = 0;
-    my $lead= "and (\n";
-    foreach my $field ("assigned_to", "reporter", "cc", "qa_contact",
-                       "longdesc") {
-        my $doit = $::FORM{"email$field$id"};
-        if (!$doit) {
-            next;
-        }
-        $foundone = 1;
-        my $table;
-        if ($field eq "assigned_to") {
-            $table = "assign";
-        } elsif ($field eq "reporter") {
-            $table = "report";
-        } elsif ($field eq "qa_contact") {
-            $table = "qacont";
-        } elsif ($field eq "longdesc") {
-            $table = "longdescname";
-            $needlongdescs = 1;
-        } else {
-            $table = "ccname";
-        }
-        if ($type eq "exact") {
-            if ($field eq "cc") {
-                $query .= "\t$lead cc.who = $emailid\n";
-            } elsif ($field eq "longdesc") {
-                $query .= "\t$lead longdescs.who = $emailid\n";
-            } else {
-                $query .= "\t$lead $field = $emailid\n";
-            }
-        } elsif ($type eq "regexp") {
-            $query .= "\t$lead $table.login_name regexp $qemail\n";
-        } elsif ($type eq "notregexp") {
-            $query .= "\t$lead $table.login_name not regexp $qemail\n";
-        } else {
-            $query .= "\t$lead instr($table.login_name, $qemail)\n";
-        }
-        $lead = " or ";
-    }
-    if (!$foundone) {
-        print "\n\n<P>You must specify one or more fields in which to search for <tt>$email</tt>.\n";
-        print "<P>Please click the <B>Back</B> button and try again.\n";
-        PutFooter();
-        exit;
-    }
-    if ($lead eq " or ") {
-        $query .= ")\n";
-    }
-}
-                
-
 
 
+my $query = GenerateSQL(\@fields, undef, undef, $::buffer);
 
-if (defined $::FORM{'changedin'}) {
-    my $c = trim($::FORM{'changedin'});
-    if ($c ne "") {
-        if ($c !~ /^[0-9]*$/) {
-            print "\n\n<P>The 'changed in last ___ days' field must be a simple ";
-            print "number. You entered \"$c\", which doesn't cut it.";
-            print "<P>Please click the <B>Back</B> button and try again.\n";
-            PutFooter();
-            exit;
-        }
-        $query .= "and to_days(now()) - to_days(bugs.delta_ts) <= $c ";
-    }
-}
-
-if (defined $minvotes) {
-    $query .= "and votes >= $minvotes ";
-}
-
-
-my $ref = $::MFORM{'chfield'};
-
-
-sub SqlifyDate {
-    my ($str) = (@_);
-    if (!defined $str) {
-        $str = "";
-    }
-    my $date = str2time($str);
-    if (!defined $date) {
-        print "\n\n<P>The string '<tt>$str</tt>' is not a legal date.\n";
-        print "<P>Please click the <B>Back</B> button and try again.\n";
-        PutFooter();
-        exit;
-    }
-    return time2str("'%Y/%m/%d %H:%M:%S'", $date);
-}
-
-
-if (defined $ref) {
-    my $which = lsearch($ref, "[Bug creation]");
-    if ($which >= 0) {
-        splice(@$ref, $which, 1);
-        $query .= "and bugs.creation_ts >= " .
-            SqlifyDate($::FORM{'chfieldfrom'}) . "\n";
-        my $to = $::FORM{'chfieldto'};
-        if (defined $to) {
-            $to = trim($to);
-            if ($to ne "" && $to !~ /^now$/i) {
-                $query .= "and bugs.creation_ts <= " .
-                    SqlifyDate($to) . "\n";
-            }
-        }
-    }
-}        
-
-
-
-if (defined $ref && 0 < @$ref) {
-    # Do surgery on the query to tell it to patch in the bugs_activity
-    # table.
-    $query =~ s/where/, bugs_activity where/;
-    
-    my @list;
-    foreach my $f (@$ref) {
-        push(@list, "\nbugs_activity.fieldid = " . GetFieldID($f));
-    }
-    $query .= "and bugs_activity.bug_id = bugs.bug_id and (" .
-        join(' or ', @list) . ") ";
-    $query .= "and bugs_activity.bug_when >= " .
-        SqlifyDate($::FORM{'chfieldfrom'}) . "\n";
-    my $to = $::FORM{'chfieldto'};
-    if (defined $to) {
-        $to = trim($to);
-        if ($to ne "" && $to !~ /^now$/i) {
-            $query .= "and bugs_activity.bug_when <= " . SqlifyDate($to) . "\n";
-        }
-    }
-    my $value = $::FORM{'chfieldvalue'};
-    if (defined $value) {
-        $value = trim($value);
-        if ($value ne "") {
-            $query .= "and bugs_activity.newvalue = " .
-                SqlQuote($value) . "\n";
-        }
-    }
-}
-
-sub GetByWordList {
-    my ($field, $strs, $verb) = (@_);
-    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'");
-        }
-    }
-
-    if (0 == @list) {
-        return "";
-    }
-
-    return "and (" . join(" $verb ", @list) . ")\n";
-}
-
-foreach my $f ("short_desc", "long_desc", "bug_file_loc",
-               "status_whiteboard") {
-    if (defined $::FORM{$f}) {
-        my $s = trim($::FORM{$f});
-        if ($s ne "") {
-            my $n = $f;
-            my $q = SqlQuote($s);
-            my $type = $::FORM{$f . "_type"};
-            if ($f eq "long_desc") {
-                $needlongdescs = 1; # Patch in the longdescs table.
-                $query .= "and longdescs.bug_id = bugs.bug_id\n";
-                $n = "longdescs.thetext";
-            }
-            if ($type eq "regexp") {
-                $query .= "and $n regexp $q\n";
-            } elsif ($type eq "notregexp") {
-                $query .= "and $n not regexp $q\n";
-            } elsif ($type eq "casesubstring") {
-                $query .= "and instr($n, $q)\n";
-            } elsif ($type eq "allwords") {
-                $query .= GetByWordList($n, $s, "and");
-            } elsif ($type eq "anywords") {
-                $query .= GetByWordList($n, $s, "or");
-            } else {
-                $query .= "and instr(lower($n), lower($q))\n";
-            }
-        }
-    }
-}
-
-if ($needlongdescs) {
-    $query =~ s/where/, longdescs left join profiles longdescname on longdescs.who = longdescname.userid where/;
-    $query .= " AND longdescs.bug_id = bugs.bug_id ";
-}
-
-
-
-$query .= "group by bugs.bug_id\n";
 
 
 if ($::COOKIE{'LASTORDER'}) {
@@ -644,9 +847,12 @@ if ($::COOKIE{'LASTORDER'}) {
 
 
 if (defined $::FORM{'order'} && $::FORM{'order'} ne "") {
-    $query .= "order by ";
+    $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.
@@ -661,18 +867,18 @@ if (defined $::FORM{'order'} && $::FORM{'order'} ne "") {
             last ORDER;
         };
         /Assign/ && do {
-            $::FORM{'order'} = "assign.login_name, bugs.bug_status, priority, bugs.bug_id";
+            $::FORM{'order'} = "map_assigned_to.login_name, bugs.bug_status, priority, bugs.bug_id";
             last ORDER;
         };
         # DEFAULT
-        $::FORM{'order'} = "bugs.bug_status, bugs.priority, assign.login_name, bugs.bug_id";
+        $::FORM{'order'} = "bugs.bug_status, bugs.priority, map_assigned_to.login_name, bugs.bug_id";
     }
     $query .= $::FORM{'order'};
 }
 
 
 if ($::FORM{'debug'} && $serverpush) {
-    print "<PRE>$query</PRE>\n";
+    print "<P><CODE>" . value_quote($query) . "</CODE><P>\n";
 }
 
 
@@ -866,7 +1072,7 @@ print "
 <B>" .  time2str("%a %b %e %T %Z %Y", time()) . "</B>";
 
 if (defined $::FORM{'debug'}) {
-    print "<PRE>$query</PRE>\n";
+    print "<P><CODE>" . value_quote($query) . "</CODE><P>\n";
 }
 
 if ($toolong) {
index f3937c0d16b7c3f8ba96cec8839e721f12895d3e..ed25386682a2a96975a23168a7cf8c8074008b15 100755 (executable)
@@ -882,19 +882,24 @@ AddFDef("short_desc", "Summary", 1);
 AddFDef("product", "Product", 1);
 AddFDef("version", "Version", 1);
 AddFDef("rep_platform", "Platform", 1);
+AddFDef("bug_file_loc", "URL", 1);
 AddFDef("op_sys", "OS/Version", 1);
 AddFDef("bug_status", "Status", 1);
+AddFDef("status_whiteboard", "Status Whiteboard", 1);
+AddFDef("keywords", "Keywords", 1);
 AddFDef("resolution", "Resolution", 1);
 AddFDef("bug_severity", "Severity", 1);
 AddFDef("priority", "Priority", 1);
 AddFDef("component", "Component", 1);
 AddFDef("assigned_to", "AssignedTo", 1);
 AddFDef("reporter", "ReportedBy", 1);
+AddFDef("votes", "Votes", 0);
 AddFDef("qa_contact", "QAContact", 0);
 AddFDef("cc", "CC", 0);
 AddFDef("dependson", "BugsThisDependsOn", 0);
 AddFDef("blocked", "OtherBugsDependingOnThis", 0);
 AddFDef("target_milestone", "Target Milestone", 0);
+AddFDef("longdesc", "Comment", 0);
     
     
 
index 5ed72da9b590189ea785c12127baa153f187a266..6e3b2bd6689c30925bbee279c2264de6b164dabc 100755 (executable)
--- a/query.cgi
+++ b/query.cgi
@@ -468,7 +468,7 @@ print "
 
 </td>
 <td align=left valign=top>
-@{[make_selection_widget(\"platform\",\@::legal_platform,$default{'platform'}, $type{'platform'}, 1)]}
+@{[make_selection_widget(\"rep_platform\",\@::legal_platform,$default{'platform'}, $type{'platform'}, 1)]}
 
 </td>
 <td align=left valign=top>
@@ -663,6 +663,104 @@ print "
 <p>
 ";
 
+
+my @fields;
+push(@fields, ["noop", "---"]);
+SendSQL("SELECT name, description FROM fielddefs ORDER BY sortkey");
+while (MoreSQLData()) {
+    my ($name, $description) = (FetchSQLData());
+    push(@fields, [$name, $description]);
+}
+
+my @types = (
+            ["noop", "---"],
+            ["equals", "equal to"],
+            ["notequals", "not equal to"],
+            ["casesubstring", "contains (case-sensitive) substring"],
+            ["substring", "contains (case-insensitive) substring"],
+            ["notsubstring", "does not contain (case-insensitive) substring"],
+            ["regexp", "contains regexp"],
+            ["notregexp", "does not contain regexp"],
+            ["lessthan", "less than"],
+            ["greaterthan", "greater than"],
+            ["anywords", "any words"],
+            ["allwords", "all words"],
+            ["nowords", "none of the words"],
+            ["changedbefore", "changed before"],
+            ["changedafter", "changed after"],
+            ["changedto", "changed to"],
+            ["changedby", "changed by"],
+            );
+
+
+foreach my $cmd (grep(/^cmd-/, keys(%::FORM))) {
+    if ($cmd =~ /^cmd-add(\d+)-(\d+)-(\d+)$/) {
+       $::FORM{"field$1-$2-$3"} = "xyzzy";
+    }
+}
+       
+#  foreach my $i (sort(keys(%::FORM))) {
+#      print "$i : " . value_quote($::FORM{$i}) . "<BR>\n";
+#  }
+
+
+if (!exists $::FORM{'field0-0-0'}) {
+    $::FORM{'field0-0-0'} = "xyzzy";
+}
+
+print qq{<A NAME="chart"> </A>\n};
+
+my $chart;
+for ($chart=0 ; exists $::FORM{"field$chart-0-0"} ; $chart++) {
+    my @rows;
+    my $row;
+    for ($row = 0 ; exists $::FORM{"field$chart-$row-0"} ; $row++) {
+       my @cols;
+       my $col;
+       for ($col = 0 ; exists $::FORM{"field$chart-$row-$col"} ; $col++) {
+           my $key = "$chart-$row-$col";
+           my $deffield = $::FORM{"field$key"} || "";
+           my $deftype = $::FORM{"type$key"} || "";
+           my $defvalue = value_quote($::FORM{"value$key"} || "");
+           my $line = "";
+           $line .= "<TD>";
+           $line .= BuildPulldown("field$key", \@fields, $deffield);
+           $line .= BuildPulldown("type$key", \@types, $deftype);
+           $line .= qq{<INPUT NAME="value$key" VALUE="$defvalue">};
+           $line .= "</TD>\n";
+           push(@cols, $line);
+       }
+       push(@rows, "<TR>" . join(qq{<TD ALIGN="center"> or </TD>\n}, @cols) .
+            qq{<TD><INPUT TYPE="submit" VALUE="Or" NAME="cmd-add$chart-$row-$col"></TD></TR>});
+    }
+    print qq{
+<HR>
+<TABLE>
+};
+    print join('<TR><TD>And</TD></TR>', @rows);
+    print qq{
+<TR><TD><INPUT TYPE="submit" VALUE="And" NAME="cmd-add$chart-$row-0">
+};
+    my $n = $chart + 1;
+    if (!exists $::FORM{"field$n-0-0"}) {
+        print qq{
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<INPUT TYPE="submit" VALUE="Add another boolean chart" NAME="cmd-add$n-0-0">
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<NOBR><A HREF="booleanchart.html">What is this stuff?</A></NOBR>
+};
+    }
+    print qq{
+</TD>
+</TR>
+</TABLE>
+    };
+}
+print qq{<HR>};
+
+
+
+
 if (!$userid) {
     print qq{<INPUT TYPE="hidden" NAME="cmdtype" VALUE="doit">};
 } else {