]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 138995 - split up search.html.tmpl. Patch by gerv; 2xr=myk.
authorgerv%gerv.net <>
Sat, 27 Apr 2002 03:03:07 +0000 (03:03 +0000)
committergerv%gerv.net <>
Sat, 27 Apr 2002 03:03:07 +0000 (03:03 +0000)
template/en/default/search/boolean-charts.html.tmpl [new file with mode: 0644]
template/en/default/search/form.html.tmpl [new file with mode: 0644]
template/en/default/search/knob.html.tmpl [new file with mode: 0644]
template/en/default/search/search-advanced.html.tmpl
template/en/default/search/search.html.tmpl

diff --git a/template/en/default/search/boolean-charts.html.tmpl b/template/en/default/search/boolean-charts.html.tmpl
new file mode 100644 (file)
index 0000000..2d25f91
--- /dev/null
@@ -0,0 +1,118 @@
+<!-- 1.0@bugzilla.org -->
+[%# 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): Gervase Markham <gerv@gerv.net>
+  #%]
+  
+[% types = [
+  { name => "noop", description => "---" },
+  { name => "equals", description => "is equal to" },
+  { name => "notequals", description => "is not equal to" },
+  { name => "substring", description => "contains the string" },
+  { name => "casesubstring", description => "contains the string (exact case)" },
+  { name => "notsubstring", description => "does not contain the string" },
+  { name => "allwordssubstr", description => "contains all of the strings" },
+  { name => "anywordssubstr", description => "contains any of the strings" },
+  { name => "regexp", description => "contains regexp" },
+  { name => "notregexp", description => "does not contain regexp" },
+  { name => "lessthan", description => "is less than" },
+  { name => "greaterthan", description => "is greater than" },
+  { name => "anywords", description => "contains any of the words" },
+  { name => "allwords", description => "contains all of the words" },
+  { name => "nowords", description => "contains none of the words" },
+  { name => "changedbefore", description => "changed before" },
+  { name => "changedafter", description => "changed after" },
+  { name => "changedfrom", description => "changed from" },
+  { name => "changedto", description => "changed to" },
+  { name => "changedby", description => "changed by" } ] %]
+
+  <p>
+    <strong>
+      <a name="chart" href="queryhelp.cgi#advancedquerying">
+      Advanced Querying Using Boolean Charts</a>:
+    </strong>
+  </p>
+
+[%# Whoever wrote the original version of boolean charts had a seriously twisted mind %]
+
+[% jsmagic = "onclick=\"document.forms[0].action='query.cgi#chart'; document.forms[0].method='POST'; return 1;\"" %]
+
+[% FOREACH chart = default.charts %]
+  [% chartnum = loop.count - 1 %]
+  <table>
+  [% FOREACH row = chart %]
+    [% rownum = loop.count - 1 %]
+    <tr>
+    [% FOREACH col = row %]
+      [% colnum = loop.count - 1 %]
+      <td>
+        <select name="[% "field${chartnum}-${rownum}-${colnum}" %]">
+          [% FOREACH field = fields %]
+            <option value="[% field.name %]"
+              [%- " selected" IF field.name == col.field %]>[% field.description %]</option>
+          [% END %]
+        </select>
+
+        <select name="[% "type${chartnum}-${rownum}-${colnum}" %]">
+          [% FOREACH type = types %]
+            <option value="[% type.name %]"
+              [%- " selected" IF type.name == col.type %]>[% type.description %]</option>
+          [% END %]
+        </select>
+
+        <input name="[% "value${chartnum}-${rownum}-${colnum}" %]" 
+               value="[% col.value FILTER html %]"> 
+      </td>
+      
+      [% IF NOT col == row.last %]
+        <td align="center"> 
+          Or 
+        </td>    
+      [% ELSE %]
+        <td>
+          [% newor = colnum + 1 %]
+          <input type="submit" value="Or" 
+                 name="cmd-add[% "${chartnum}-${rownum}-${newor}" %]" [% $jsmagic %]>
+        </td>
+      [% END %]
+      
+    [% END %]
+    </tr>
+    
+    [% IF NOT row == chart.last %]
+    <tr>
+      <td>And</td>
+    </tr>    
+    [% ELSE %]
+    <tr>
+      <td>
+        [% newand = rownum + 1; newchart = chartnum + 1 %]
+        <input type="submit" value="And" 
+               name="cmd-add[% "${chartnum}-${newand}-0" %]"[% $jsmagic %]> 
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        <input type="submit" value="Add another boolean chart" 
+               name="cmd-add[% newchart %]-0-0" [% $jsmagic %]>
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+      </td>
+    </tr>   
+    [% END %]
+    
+  [% END %]
+  </table>
+  <hr>
+[% END %]
diff --git a/template/en/default/search/form.html.tmpl b/template/en/default/search/form.html.tmpl
new file mode 100644 (file)
index 0000000..9160397
--- /dev/null
@@ -0,0 +1,670 @@
+<!-- 1.0@bugzilla.org -->
+[%# 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): Chris Lahey <clahey@ximian.com> [javascript fixes]
+  #                 Christian Reis <kiko@async.com.br> [javascript rewrite]
+  #                 Gervase Markham <gerv@gerv.net>
+  #%]
+  
+  [%# Note: use Template comments and not JS ones here, to avoid bloating
+    what we actually send to the browser %]
+
+<script language="JavaScript" type="text/javascript"> <!--
+
+var first_load = true;         [%# is this the first time we load the page? %]
+var last_sel = new Array();    [%# caches last selection %]
+
+var cpts = new Array();
+var vers = new Array();
+[% IF Param('usetargetmilestone') %]
+var tms = new Array();
+[% END %]
+
+[%# Create three arrays of components, versions and target milestones, indexed 
+  # numerically according to the product they refer to. #%]
+
+[% n = 0 %]
+[% FOREACH p = product %]
+  cpts[[% n %]] = [ 
+    [%- FOREACH item = componentsbyproduct.$p %]'[% item FILTER js %]', [%- END -%]];
+  vers[[% n %]] = [ 
+    [%- FOREACH item = versionsbyproduct.$p -%]'[%  item FILTER js %]', [%- END -%]];
+  [% IF Param('usetargetmilestone') %]
+  tms[[% n %]]  = [ 
+     [%- FOREACH item = milestonesbyproduct.$p %]'[% item FILTER js %]', [%- END -%]];
+  [% END %]
+  [% n = n+1 %]
+[% END %]
+
+[%# updateSelect(array, sel, target, merging)
+  #
+  # Adds to the target select object all elements in array that
+  # correspond to the elements selected in source.
+  # - array should be a array of arrays, indexed by number. the
+  #   array should contain the elements that correspond to that
+  #   product. 
+  # - sel is a list of selected items, either whole or a diff
+  #   depending on merging.
+  # - target should be the target select object.
+  # - merging (boolean) determines if we are mergine in a diff or
+  #   substituting the whole selection. a diff is used to optimize adding
+  #   selections.
+  #
+  # Example (compsel is a select form control)
+  #
+  #     var components = Array();
+  #     components[1] = [ 'ComponentA', 'ComponentB' ];
+  #     components[2] = [ 'ComponentC', 'ComponentD' ];
+  #     source = [ 2 ];
+  #     updateSelect(components, source, compsel, 0, 0);
+  #
+  # would clear compsel and add 'ComponentC' and 'ComponentD' to it.
+  #
+  %]
+
+function updateSelect(array, sel, target, merging) {
+        
+    var i, item;
+
+    [%# If we have no versions/components/milestones %]
+    if (array.length < 1) {
+        target.options.length = 0;
+        return false;
+    }
+
+    if (merging) {
+        [%# array merging/sorting in the case of multiple selections %]
+        [%# merge in the current options with the first selection %]
+        item = merge_arrays(array[sel[0]], target.options, 1);
+
+        [%# merge the rest of the selection with the results %]
+        for (i = 1 ; i < sel.length ; i++) {
+            item = merge_arrays(array[sel[i]], item, 0);
+        }
+    } else if ( sel.length > 1 ) {
+        [%# here we micro-optimize for two arrays to avoid merging with a
+            null array %]
+        item = merge_arrays(array[sel[0]],array[sel[1]], 0);
+
+        [%# merge the arrays. not very good for multiple selections. %]
+        for (i = 2; i < sel.length; i++) {
+            item = merge_arrays(item, array[sel[i]], 0);
+        }
+    } else { [%# single item in selection, just get me the list %]
+        item = array[sel[0]];
+    }
+
+    [%# clear select %]
+    target.options.length = 0;
+
+    [%# load elements of list into select %]
+    for (i = 0; i < item.length; i++) {
+        target.options[i] = new Option(item[i], item[i]);
+    }
+    return true;
+}
+
+[%# Returns elements in a that are not in b. 
+  # NOT A REAL DIFF: does not check the reverse.
+  #    - a,b: arrays of values to be compare. %]
+function fake_diff_array(a, b) {
+    var newsel = new Array();
+    var found = false;
+
+    [%# do a boring array diff to see who's new %]
+    for (var ia in a) {
+        for (var ib in b) {
+            if (a[ia] == b[ib]) {
+                found = true;
+            }
+        }
+        if (!found) {
+            newsel[newsel.length] = a[ia];
+        }
+        found = false;
+    }
+    return newsel;
+}
+
+[%# takes two arrays and sorts them by string, returning a new, sorted
+  # array. the merge removes dupes, too.
+  #    - a, b: arrays to be merge.
+  #    - b_is_select: if true, then b is actually an optionitem and as
+  #      such we need to use item.value on it. %]
+function merge_arrays(a, b, b_is_select) {
+    var pos_a = 0;
+    var pos_b = 0;
+    var ret = new Array();
+    var bitem, aitem;
+
+    [%# iterate through both arrays and add the larger item to the return
+       list. remove dupes, too. Use toLowerCase to provide
+       case-insensitivity. %]
+    while ((pos_a < a.length) && (pos_b < b.length)) {
+        if (b_is_select) {
+            bitem = b[pos_b].value;
+        } else {
+            bitem = b[pos_b];
+        }
+        aitem = a[pos_a];
+
+        [%# smaller item in list a %]
+        if (aitem.toLowerCase() < bitem.toLowerCase()) {
+            ret[ret.length] = aitem;
+            pos_a++;
+        } else {
+            [%# smaller item in list b %]
+            if (aitem.toLowerCase() > bitem.toLowerCase()) {
+                ret[ret.length] = bitem;
+                pos_b++;
+            } else {
+                [%# list contents are equal, inc both counters. %]
+                ret[ret.length] = aitem;
+                pos_a++;
+                pos_b++;
+            }
+        }
+    }
+
+    [%# catch leftovers here. these sections are ugly code-copying. %]
+    if (pos_a < a.length) {
+        for (; pos_a < a.length ; pos_a++) {
+            ret[ret.length] = a[pos_a];
+        }
+    }
+
+    if (pos_b < b.length) {
+        for (; pos_b < b.length; pos_b++) {
+            if (b_is_select) {
+                bitem = b[pos_b].value;
+            } else {
+                bitem = b[pos_b];
+            }
+            ret[ret.length] = bitem;
+        }
+    }
+    return ret;
+}
+
+[%# Returns an array of indexes or values from a select form control.
+  #    - control: select control from which to find selections
+  #    - findall: boolean, store all options when true or just the selected 
+  #      indexes
+  #    - want_values: boolean; we store values when true and indexes when
+  #      false %]
+function getSelection(control, findall, want_values) {
+    var ret = new Array();
+
+    if ((!findall) && (control.selectedIndex == -1)) {
+        return ret;
+    }
+
+    for (var i=0; i<control.length; i++) {
+        if (findall || control.options[i].selected) {
+            ret[ret.length] = want_values ? control.options[i].value : i;
+        }
+    }
+    return ret;
+}
+
+[%# Selects items in control that have index defined in sel
+  #    - control: SELECT control to be restored
+  #    - selnames: array of indexes in select form control %]
+function restoreSelection(control, selnames) {
+    [%# right. this sucks. but I see no way to avoid going through the
+      # list and comparing to the contents of the control. %]
+    for (var j=0; j < selnames.length; j++) {
+        for (var i=0; i < control.options.length; i++) {
+            if (control.options[i].value == selnames[j]) {
+                control.options[i].selected = true;
+            }
+        }
+    }
+}
+
+[%# selectProduct reads the selection from f.product and updates
+  # f.version, component and target_milestone accordingly.
+  #     - f: a form containing product, component, varsion and
+  #       target_milestone select boxes.
+  # globals (3vil!):
+  #     - cpts, vers, tms: array of arrays, indexed by product name. the
+  #       subarrays contain a list of names to be fed to the respective
+  #       selectboxes. For bugzilla, these are generated with perl code
+  #       at page start.
+  #     - first_load: boolean, specifying if it is the first time we load
+  #       the query page.
+  #     - last_sel: saves our last selection list so we know what has
+  #       changed, and optimize for additions. %]
+function selectProduct(f) {
+    [%# this is to avoid handling events that occur before the form
+        itself is ready, which could happen in buggy browsers. %]
+    if ((!f) || (!f.product)) {
+        return;
+    }
+
+    [%# if this is the first load and nothing is selected, no need to
+        merge and sort all components; perl gives it to us sorted. %]
+    if ((first_load) && (f.product.selectedIndex == -1)) {
+        first_load = false;
+        return;
+    }
+    
+    [%# turn first_load off. this is tricky, since it seems to be
+        redundant with the above clause. It's not: if when we first load
+        the page there is _one_ element selected, it won't fall into that
+        clause, and first_load will remain 1. Then, if we unselect that
+        item, selectProduct will be called but the clause will be valid
+        (since selectedIndex == -1), and we will return - incorrectly -
+        without merge/sorting. %]
+    first_load = false;
+
+    [%# - sel keeps the array of products we are selected. 
+        - merging says if it is a full list or just a list of products that
+          were added to the current selection. %]
+    var merging = false;
+    var sel = Array();
+
+    [%# if nothing selected, pick all %]
+    var findall = f.product.selectedIndex == -1;
+    sel = getSelection(f.product, findall, false);
+    if (!findall) {
+        [%# save sel for the next invocation of selectProduct() %]
+        var tmp = sel;
+    
+        [%# this is an optimization: if we have just added products to an
+            existing selection, no need to clear the form controls and add 
+            everybody again; just merge the new ones with the existing 
+            options. %]
+        if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
+            sel = fake_diff_array(sel, last_sel);
+            merging = true;
+        }
+        last_sel = tmp;
+    }
+    [%# save original options selected %]
+    var saved_cpts = getSelection(f.component, false, true);
+    var saved_vers = getSelection(f.version, false, true);
+    [% IF Param('usetargetmilestone') %]
+    var saved_tms = getSelection(f.target_milestone, false, true);
+    [% END %]
+
+    [%# do the actual fill/update, reselect originally selected options %]
+    updateSelect(cpts, sel, f.component, merging);
+    restoreSelection(f.component, saved_cpts);
+    updateSelect(vers, sel, f.version, merging);
+    restoreSelection(f.version, saved_vers);
+    [% IF Param('usetargetmilestone') %]
+    updateSelect(tms, sel, f.target_milestone, merging);
+    restoreSelection(f.target_milestone, saved_tms);
+    [% END %]
+}
+
+// -->
+</script>
+
+[% query_variants = [ 
+  { value => "allwordssubstr", description => "contains all of the words/strings" },
+  { value => "anywordssubstr", description => "contains any of the words/strings" },
+  { value => "substring", description => "contains the string" },
+  { value => "casesubstring", description => "contains the string (exact case)" },
+  { value => "allwords", description => "contains all of the words" },
+  { value => "anywords", description => "contains any of the words" },
+  { value => "regexp", description => "matches the regexp" },
+  { value => "notregexp", description => "doesn&#8217;t match the regexp" } ] %]
+     
+[%# *** Summary *** %]
+
+<table>
+  <tr>
+    <th align="right">Summary:</th>
+    <td>
+      <select name="short_desc_type">
+      [% FOREACH qv = query_variants %]
+        <option value="[% qv.value %]"
+          [% " selected" IF default.short_desc_type.0 == qv.value %]>[% qv.description %]</option>
+      [% END %]              
+      </select>
+    </td>
+    <td>
+      <input name="short_desc" size="40" value="[% default.short_desc.0 FILTER html %]">
+    </td>
+    <td>
+      <input type="submit" value="[% button_name %]">
+    </td>
+  </tr>  
+
+[%# *** Product Component Version Target *** %]
+
+  <tr>
+    <td colspan="4">
+      <table>
+        <tr valign="bottom">
+          <th align="left">Product:</th>
+          <th align="left"><a href="describecomponents.cgi">Component</a>:</th>
+          <th align="left">Version:</th>
+
+        [% IF (Param("usetargetmilestone")) %]
+          <th align="left">Target:</th>
+        [% END %]
+        </tr>
+
+        <tr valign="top">
+
+          [%# Can't use the select block here because of onChange and the fact that
+              'component' is a toolkit reserved word - we use 'component_' instead. %]
+          <td align="left">
+            <select name="product" multiple size="5" onChange="selectProduct(this.form);">
+            [% FOREACH p = product %]
+              <option value="[% p FILTER html %]"
+                [% " selected" IF lsearch(default.product, p) != -1 %]>
+                [% p FILTER html %]</option>
+            [% END %]
+            </select>
+          </td>
+
+          <td align="left">
+            <select name="component" multiple size="5">
+            [% FOREACH c = component_ %]
+              <option value="[% c FILTER html %]"
+                [% " selected" IF lsearch(default.component, c) != -1 %]>
+                [% c FILTER html %]</option>
+            [% END %]
+            </select>
+          </td>
+
+          [% PROCESS select sel = { name => 'version', size => 5 } %]
+
+        [% IF Param('usetargetmilestone') && target_milestone.size > 0 %]
+          [% PROCESS select sel = { name => 'target_milestone', size => 5 } %]
+        [% END %]
+        </tr>
+      </table>
+    </td>
+  </tr>
+  
+[%# *** Comment URL Whiteboard Keywords *** %]
+
+  [% FOREACH field = [ 
+    { name => "long_desc", description => "A&nbsp;comment" },
+    { name => "bug_file_loc", description => "The&nbsp;URL" },
+    { name => "status_whiteboard", description => "Whiteboard" } ] %]
+
+    [% UNLESS field.name == 'status_whiteboard' AND NOT Param('usestatuswhiteboard') %]
+    <tr>
+      <th align="right">[% field.description %]:</th>
+      <td>
+        <select name="[% field.name %]_type">
+        [% FOREACH qv = query_variants %]
+          [% type = "${field.name}_type" %]                   
+          <option value="[% qv.value %]"
+            [% " selected" IF default.$type.0 == qv.value %]>[% qv.description %]</option>
+        [% END %]              
+        </select>
+      </td>
+      <td><input name="[% field.name %]" size="40" value="
+        [% default.${field.name}.0 FILTER html %]">
+      </td>
+      <td></td>
+    </tr>  
+    [% END %]
+  [% END %]
+
+  [% IF have_keywords %]
+    <tr>
+      <th align="right"><a href="describekeywords.cgi">Keywords</a>:</th>
+      <td>
+        <select name="keywords_type">
+        [% FOREACH qv = [ 
+          { name => "anywords", description => "contains any of the keywords" },
+          { name => "allwords", description => "contains all of the keywords" },
+          { name => "nowords",  description => "contains none of the keywords" } ] %]
+
+          <option value="[% qv.name %]"
+            [% " selected" IF default.keywords_type.0 == qv.name %]>
+            [% qv.description %]</option>
+        [% END %]
+        </select>
+      </td>
+      <td>
+        <input name="keywords" size="40" value="[% default.keywords.0 FILTER html %]">
+      </td>
+    </tr>
+  [% END %]
+</table>  
+
+<hr>
+
+[%# *** Status Resolution Severity Priority Hardware OS *** %]
+
+<table>
+  <tr>
+    <th align="left"><a href="queryhelp.cgi#status">Status</a>:</th>
+    <th align="left"><a href="queryhelp.cgi#resolution">Resolution</a>:</th>
+    <th align="left"><a href="queryhelp.cgi#severity">Severity</a>:</th>
+    <th align="left"><a href="queryhelp.cgi#priority">Priority</a>:</th>
+    <th align="left"><a href="queryhelp.cgi#platform">Hardware</a>:</th>
+    <th align="left"><a href="queryhelp.cgi#opsys">OS</a>:</th>
+  </tr>
+
+  <tr valign="top">
+    [% PROCESS select sel = { name => 'bug_status', size => 7 } %]
+    [% PROCESS select sel = { name => 'resolution', size => 7 } %]
+    [% PROCESS select sel = { name => 'bug_severity', size => 7 } %]    
+    [% PROCESS select sel = { name => 'priority', size => 7 } %]    
+    [% PROCESS select sel = { name => 'rep_platform', size => 7 } %]
+    [% PROCESS select sel = { name => 'op_sys', size => 7 } %]
+  </tr>
+</table>
+
+<p>
+
+[%# *** Email Numbering Votes *** %]
+
+<table>
+  <tr>
+    <td>
+      <fieldset>
+        <legend>
+          <strong>
+            <a href="queryhelp.cgi#peopleinvolved">Email</a> and Numbering
+          </strong>  
+        </legend>
+
+
+<table>
+  <tr>
+  [% FOREACH n = [1, 2] %]
+    <td>
+
+
+<table cellspacing="0" cellpadding="0">
+  <tr>
+    <td>
+      Any of:
+    </td>
+  </tr>
+  <tr>
+    <td>
+      <input type="checkbox" name="emailassigned_to[% n %]" 
+             id="emailassigned_to[% n %]" value="1"
+             [% " checked" IF default.emailassigned_to.$n %]>
+      <label for="emailassigned_to[% n %]">
+        bug owner
+      </label>
+    </td>
+  </tr>
+  <tr>
+    <td>
+      <input type="checkbox" name="emailreporter[% n %]" 
+             id="emailreporter[% n %]" value="1"
+             [% " checked" IF default.emailreporter.$n %]>
+      <label for="emailreporter[% n %]">
+        reporter
+      </label>
+    </td>
+  </tr>
+  [% IF Param('useqacontact') %]
+  <tr>
+    <td>
+      <input type="checkbox" name="emailqa_contact[% n %]" 
+             id="emailqa_contact[% n %]" value="1"
+             [% " checked" IF default.emailqa_contact.$n %]>
+      <label for="emailqa_contact[% n %]">
+        QA contact
+      </label>
+    </td>
+  </tr>
+  [% END %]
+  <tr>
+    <td>
+      <input type="checkbox" name="emailcc[% n %]" 
+             id="emailcc[% n %]" value="1"
+             [% " checked" IF default.emailcc.$n %]>
+      <label for="emailcc[% n %]">
+        CC list member
+      </label>
+    </td>
+  </tr>
+  <tr>
+    <td>
+        <input type="checkbox" name="emaillongdesc[% n %]" 
+               id="emaillongdesc[% n %]" value="1"
+               [% " checked" IF default.emaillongdesc.$n %]>
+      <label for="emaillongdesc[% n %]">
+        commenter
+      </label>
+    </td>
+  </tr>
+  <tr>
+    <td>
+      <select name="emailtype[% n %]">
+      [% FOREACH qv = [ 
+        { name => "substring", description => "contains" },
+        { name => "exact", description => "is" },
+        { name => "regexp", description => "matches regexp" },
+        { name => "notregexp", description => "doesn&#8217;t match regexp" } ] %]
+        
+        <option value="[% qv.name %]"
+          [% " selected" IF default.emailtype.$n == qv.name %]>[% qv.description %]</option>
+      [% END %]
+      </select>
+    </td>
+  </tr>
+  <tr>
+    <td>
+      <input name="email[% n %]" size="25" value="[% default.email.$n FILTER html %]">
+    </td>
+  </tr>
+</table>
+
+    
+    </td>
+  [% END %]
+  </tr>
+</table>
+<hr>
+<table>  
+  <tr>
+    <td>
+      <select name="bugidtype">
+        <option value="include"[% " selected" IF default.bugidtype.0 == "include" %]>Only include</option>
+        <option value="exclude"[% " selected" IF default.bugidtype.0 == "exclude" %]>Exclude</option>
+      </select>
+      bugs numbered: 
+    </td>
+    <td>
+      <input type="text" name="bug_id" value="[% default.bug_id.0 FILTER html %]" size="20">
+    </td>
+  </tr>
+  <tr>
+    <td></td>
+    <td>(comma-separated list)</td>
+  </tr>
+  <tr>
+    <td align="right">
+      Only bugs with at least:
+    </td>
+    <td>
+      <input name="votes" size="3" value="[% default.votes.0 FILTER html %]"> votes
+    </td>
+  </tr>
+</table>
+
+
+      </fieldset>
+    </td>
+   
+[%# *** Bug Changes *** %]
+
+    <td valign="top">
+      <fieldset>
+        <legend><strong>Bug Changes</strong></legend>
+
+
+<dl>
+  <dt>Only bugs changed in the last </dt>
+  <dd><input name=changedin size=3 value="[% default.changedin.0 FILTER html %]"> days</dd>
+</dl>
+
+<dl>
+  <dt>Only bugs where any of the fields</dt>
+  <dd>
+    <select name="chfield" multiple size="4">
+    [% FOREACH field = chfield %]
+      <option value="[% field FILTER html %]"
+        [% " selected" IF lsearch(default.chfield, field) != -1 %]>
+        [% field FILTER html %]</option>
+    [% END %]
+    </select>
+  </dd>
+
+  <dt>were changed between</dt>
+  <dd>
+    <input name="chfieldfrom" size="10" value="[% default.chfieldfrom.0 FILTER html %]">
+    and <input name="chfieldto" size="10" value="[% default.chfieldto.0 FILTER html %]">
+    <br>(YYYY-MM-DD)
+  </dd>
+  <dt>to this value: (optional)</dt>
+  <dd>
+    <input name="chfieldvalue" size="20" value="[% default.chfieldvalue.0 FILTER html %]">
+  </dd>
+</dl>
+
+
+       </fieldset>
+     </td>
+  </tr>
+</table>
+
+[%# Note: the <form> tag is unclosed at the end of this template %]
+
+[%############################################################################%]
+[%# Block for SELECT fields                                                  #%]
+[%############################################################################%]
+
+[% BLOCK select %]
+  <td align="left">
+    <select name="[% sel.name %]" multiple size="[% sel.size %]">
+    [% FOREACH name = ${sel.name} %]
+      <option value="[% name FILTER html %]"
+        [% " selected" IF lsearch(default.${sel.name}, name) != -1 %]>
+        [% name FILTER html %]</option>
+    [% END %]
+    </select>
+  </td>
+[% END %]
diff --git a/template/en/default/search/knob.html.tmpl b/template/en/default/search/knob.html.tmpl
new file mode 100644 (file)
index 0000000..beda930
--- /dev/null
@@ -0,0 +1,95 @@
+<!-- 1.0@bugzilla.org -->
+[%# 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): Gervase Markham <gerv@gerv.net>
+  #%]
+  
+<table>
+  <tr>
+    <td colspan="2">
+
+    [% IF NOT userid %]
+      <input type="hidden" name="cmdtype" value="doit">
+    [% ELSE %]
+      <br>
+      <input type="radio" name="cmdtype" value="doit" checked> Run this query
+      <br>
+
+      [% IF namedqueries.size > 0 %]
+        <p>
+        <table cellspacing="0" cellpadding="0">
+          <tr>
+           <td>
+             <input type="radio" name="cmdtype" value="editnamed">
+             Load my remembered query:
+           </td>
+           <td rowspan="3">
+             <select name="namedcmd">
+             [% FOREACH query = namedqueries %]
+               <option value="[% query FILTER html %]">[% query FILTER html %]</option>
+             [% END %]
+             </select>
+            </td>
+          </tr>  
+          <tr>
+            <td>
+              <input type="radio" name="cmdtype" value="runnamed">
+              Run my remembered query:
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <input type="radio" name="cmdtype" value="forgetnamed">
+              Forget my remembered query:
+            </td>
+          </tr>
+        </table>
+        </p>
+      [% END %]
+
+      <input type="radio" name="cmdtype" value="asdefault">
+      Remember this as my default query
+      <br>
+      <input type="radio" name="cmdtype" value="asnamed">
+      Remember this query, and name it:
+      <input type="text" name="newqueryname">
+      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="checkbox" name="tofooter" value="1">
+          and put it in my page footer
+      <br>
+    [% END %]
+      <br>
+      <div>
+      Sort results by:
+      <select name="order">
+      [% FOREACH order = orders %]
+        <option value="[% order FILTER html %]"
+          [% " selected" IF default.order.0 == order %]>[% order FILTER html %]</option> 
+      [% END %]
+      </select>
+
+      <input type="submit" value="[% button_name %]">
+      [% IF userdefaultquery %]
+         <p>
+           <a href="query.cgi?nukedefaultquery=1">
+             Set my default query back to the system default</a>
+         </p>
+      [% END %]
+      </div>
+    </td>
+  </tr>
+</table>  
index ec93e85924d503b37bba08232d0dbc71477631ca..b64470937d7c3783b8ad06cfa35a77adc839c4bf 100644 (file)
@@ -16,9 +16,7 @@
   # Copyright (C) 1998 Netscape Communications Corporation. All
   # Rights Reserved.
   #
-  # Contributor(s): Chris Lahey <clahey@ximian.com> [javascript fixes]
-  #                 Christian Reis <kiko@async.com.br> [javascript rewrite]
-  #                 Gervase Markham <gerv@gerv.net>
+  # Contributor(s): Gervase Markham <gerv@gerv.net>
   #%]
 
 [% PROCESS global/header.html.tmpl 
   extra = " onLoad=\"selectProduct(document.forms['queryform']);\""
 %]
 
-[%# Note: use Template comments and not JS ones here, to avoid bloating
-    what we actually send to the browser %]
+[% button_name = "Search" %]
 
-<script language="JavaScript" type="text/javascript"> <!--
+[% PROCESS search/form.html.tmpl %]
 
-var first_load = true;         [%# is this the first time we load the page? %]
-var last_sel = new Array();    [%# caches last selection %]
-
-var cpts = new Array();
-var vers = new Array();
-[% IF Param('usetargetmilestone') %]
-var tms = new Array();
-[% END %]
-
-[%# Create three arrays of components, versions and target milestones, indexed 
-  # numerically according to the product they refer to. #%]
-
-[% n = 0 %]
-[% FOREACH p = product %]
-  cpts[[% n %]] = [ 
-    [%- FOREACH item = componentsbyproduct.$p %]'[% item FILTER js %]', [%- END -%]];
-  vers[[% n %]] = [ 
-    [%- FOREACH item = versionsbyproduct.$p -%]'[%  item FILTER js %]', [%- END -%]];
-  [% IF Param('usetargetmilestone') %]
-  tms[[% n %]]  = [ 
-     [%- FOREACH item = milestonesbyproduct.$p %]'[% item FILTER js %]', [%- END -%]];
-  [% END %]
-  [% n = n+1 %]
-[% END %]
-
-[%# updateSelect(array, sel, target, merging)
-  #
-  # Adds to the target select object all elements in array that
-  # correspond to the elements selected in source.
-  # - array should be a array of arrays, indexed by number. the
-  #   array should contain the elements that correspond to that
-  #   product. 
-  # - sel is a list of selected items, either whole or a diff
-  #   depending on merging.
-  # - target should be the target select object.
-  # - merging (boolean) determines if we are mergine in a diff or
-  #   substituting the whole selection. a diff is used to optimize adding
-  #   selections.
-  #
-  # Example (compsel is a select form control)
-  #
-  #     var components = Array();
-  #     components[1] = [ 'ComponentA', 'ComponentB' ];
-  #     components[2] = [ 'ComponentC', 'ComponentD' ];
-  #     source = [ 2 ];
-  #     updateSelect(components, source, compsel, 0, 0);
-  #
-  # would clear compsel and add 'ComponentC' and 'ComponentD' to it.
-  #
-  %]
-
-function updateSelect(array, sel, target, merging) {
-        
-    var i, item;
-
-    [%# If we have no versions/components/milestones %]
-    if (array.length < 1) {
-        target.options.length = 0;
-        return false;
-    }
-
-    if (merging) {
-        [%# array merging/sorting in the case of multiple selections %]
-        [%# merge in the current options with the first selection %]
-        item = merge_arrays(array[sel[0]], target.options, 1);
-
-        [%# merge the rest of the selection with the results %]
-        for (i = 1 ; i < sel.length ; i++) {
-            item = merge_arrays(array[sel[i]], item, 0);
-        }
-    } else if ( sel.length > 1 ) {
-        [%# here we micro-optimize for two arrays to avoid merging with a
-            null array %]
-        item = merge_arrays(array[sel[0]],array[sel[1]], 0);
-
-        [%# merge the arrays. not very good for multiple selections. %]
-        for (i = 2; i < sel.length; i++) {
-            item = merge_arrays(item, array[sel[i]], 0);
-        }
-    } else { [%# single item in selection, just get me the list %]
-        item = array[sel[0]];
-    }
-
-    [%# clear select %]
-    target.options.length = 0;
-
-    [%# load elements of list into select %]
-    for (i = 0; i < item.length; i++) {
-        target.options[i] = new Option(item[i], item[i]);
-    }
-    return true;
-}
-
-[%# Returns elements in a that are not in b. 
-  # NOT A REAL DIFF: does not check the reverse.
-  #    - a,b: arrays of values to be compare. %]
-function fake_diff_array(a, b) {
-    var newsel = new Array();
-    var found = false;
-
-    [%# do a boring array diff to see who's new %]
-    for (var ia in a) {
-        for (var ib in b) {
-            if (a[ia] == b[ib]) {
-                found = true;
-            }
-        }
-        if (!found) {
-            newsel[newsel.length] = a[ia];
-        }
-        found = false;
-    }
-    return newsel;
-}
-
-[%# takes two arrays and sorts them by string, returning a new, sorted
-  # array. the merge removes dupes, too.
-  #    - a, b: arrays to be merge.
-  #    - b_is_select: if true, then b is actually an optionitem and as
-  #      such we need to use item.value on it. %]
-function merge_arrays(a, b, b_is_select) {
-    var pos_a = 0;
-    var pos_b = 0;
-    var ret = new Array();
-    var bitem, aitem;
-
-    [%# iterate through both arrays and add the larger item to the return
-       list. remove dupes, too. Use toLowerCase to provide
-       case-insensitivity. %]
-    while ((pos_a < a.length) && (pos_b < b.length)) {
-        if (b_is_select) {
-            bitem = b[pos_b].value;
-        } else {
-            bitem = b[pos_b];
-        }
-        aitem = a[pos_a];
-
-        [%# smaller item in list a %]
-        if (aitem.toLowerCase() < bitem.toLowerCase()) {
-            ret[ret.length] = aitem;
-            pos_a++;
-        } else {
-            [%# smaller item in list b %]
-            if (aitem.toLowerCase() > bitem.toLowerCase()) {
-                ret[ret.length] = bitem;
-                pos_b++;
-            } else {
-                [%# list contents are equal, inc both counters. %]
-                ret[ret.length] = aitem;
-                pos_a++;
-                pos_b++;
-            }
-        }
-    }
-
-    [%# catch leftovers here. these sections are ugly code-copying. %]
-    if (pos_a < a.length) {
-        for (; pos_a < a.length ; pos_a++) {
-            ret[ret.length] = a[pos_a];
-        }
-    }
-
-    if (pos_b < b.length) {
-        for (; pos_b < b.length; pos_b++) {
-            if (b_is_select) {
-                bitem = b[pos_b].value;
-            } else {
-                bitem = b[pos_b];
-            }
-            ret[ret.length] = bitem;
-        }
-    }
-    return ret;
-}
-
-[%# Returns an array of indexes or values from a select form control.
-  #    - control: select control from which to find selections
-  #    - findall: boolean, store all options when true or just the selected 
-  #      indexes
-  #    - want_values: boolean; we store values when true and indexes when
-  #      false %]
-function getSelection(control, findall, want_values) {
-    var ret = new Array();
-
-    if ((!findall) && (control.selectedIndex == -1)) {
-        return ret;
-    }
-
-    for (var i=0; i<control.length; i++) {
-        if (findall || control.options[i].selected) {
-            ret[ret.length] = want_values ? control.options[i].value : i;
-        }
-    }
-    return ret;
-}
-
-[%# Selects items in control that have index defined in sel
-  #    - control: SELECT control to be restored
-  #    - selnames: array of indexes in select form control %]
-function restoreSelection(control, selnames) {
-    [%# right. this sucks. but I see no way to avoid going through the
-      # list and comparing to the contents of the control. %]
-    for (var j=0; j < selnames.length; j++) {
-        for (var i=0; i < control.options.length; i++) {
-            if (control.options[i].value == selnames[j]) {
-                control.options[i].selected = true;
-            }
-        }
-    }
-}
-
-[%# selectProduct reads the selection from f.product and updates
-  # f.version, component and target_milestone accordingly.
-  #     - f: a form containing product, component, varsion and
-  #       target_milestone select boxes.
-  # globals (3vil!):
-  #     - cpts, vers, tms: array of arrays, indexed by product name. the
-  #       subarrays contain a list of names to be fed to the respective
-  #       selectboxes. For bugzilla, these are generated with perl code
-  #       at page start.
-  #     - first_load: boolean, specifying if it is the first time we load
-  #       the query page.
-  #     - last_sel: saves our last selection list so we know what has
-  #       changed, and optimize for additions. %]
-function selectProduct(f) {
-    [%# this is to avoid handling events that occur before the form
-        itself is ready, which could happen in buggy browsers. %]
-    if ((!f) || (!f.product)) {
-        return;
-    }
-
-    [%# if this is the first load and nothing is selected, no need to
-        merge and sort all components; perl gives it to us sorted. %]
-    if ((first_load) && (f.product.selectedIndex == -1)) {
-        first_load = false;
-        return;
-    }
-    
-    [%# turn first_load off. this is tricky, since it seems to be
-        redundant with the above clause. It's not: if when we first load
-        the page there is _one_ element selected, it won't fall into that
-        clause, and first_load will remain 1. Then, if we unselect that
-        item, selectProduct will be called but the clause will be valid
-        (since selectedIndex == -1), and we will return - incorrectly -
-        without merge/sorting. %]
-    first_load = false;
-
-    [%# - sel keeps the array of products we are selected. 
-        - merging says if it is a full list or just a list of products that
-          were added to the current selection. %]
-    var merging = false;
-    var sel = Array();
-
-    [%# if nothing selected, pick all %]
-    var findall = f.product.selectedIndex == -1;
-    sel = getSelection(f.product, findall, false);
-    if (!findall) {
-        [%# save sel for the next invocation of selectProduct() %]
-        var tmp = sel;
-    
-        [%# this is an optimization: if we have just added products to an
-            existing selection, no need to clear the form controls and add 
-            everybody again; just merge the new ones with the existing 
-            options. %]
-        if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
-            sel = fake_diff_array(sel, last_sel);
-            merging = true;
-        }
-        last_sel = tmp;
-    }
-    [%# save original options selected %]
-    var saved_cpts = getSelection(f.component, false, true);
-    var saved_vers = getSelection(f.version, false, true);
-    [% IF Param('usetargetmilestone') %]
-    var saved_tms = getSelection(f.target_milestone, false, true);
-    [% END %]
-
-    [%# do the actual fill/update, reselect originally selected options %]
-    updateSelect(cpts, sel, f.component, merging);
-    restoreSelection(f.component, saved_cpts);
-    updateSelect(vers, sel, f.version, merging);
-    restoreSelection(f.version, saved_vers);
-    [% IF Param('usetargetmilestone') %]
-    updateSelect(tms, sel, f.target_milestone, merging);
-    restoreSelection(f.target_milestone, saved_tms);
-    [% END %]
-}
-
-// -->
-</script>
-
-[% query_variants = [ 
-  { value => "allwordssubstr", description => "contains all of the words/strings" },
-  { value => "anywordssubstr", description => "contains any of the words/strings" },
-  { value => "substring", description => "contains the string" },
-  { value => "casesubstring", description => "contains the string (exact case)" },
-  { value => "allwords", description => "contains all of the words" },
-  { value => "anywords", description => "contains any of the words" },
-  { value => "regexp", description => "matches the regexp" },
-  { value => "notregexp", description => "doesn&#8217;t match the regexp" } ] %]
-     
-<form method="get" action="buglist.cgi" name="queryform">
-
-[%# *** Summary *** %]
-
-<table>
-  <tr>
-    <th align="right">Summary:</th>
-    <td>
-      <select name="short_desc_type">
-      [% FOREACH qv = query_variants %]
-        <option value="[% qv.value %]"
-          [% " selected" IF default.short_desc_type.0 == qv.value %]>[% qv.description %]</option>
-      [% END %]              
-      </select>
-    </td>
-    <td>
-      <input name="short_desc" size="40" value="[% default.short_desc.0 FILTER html %]">
-    </td>
-    <td>
-      <input type="submit" value="Search">
-    </td>
-  </tr>  
-
-[%# *** Product Component Version Target *** %]
-
-  <tr>
-    <td colspan="4">
-      <table>
-        <tr valign="bottom">
-          <th align="left">Product:</th>
-          <th align="left"><a href="describecomponents.cgi">Component</a>:</th>
-          <th align="left">Version:</th>
-
-        [% IF (Param("usetargetmilestone")) %]
-          <th align="left">Target:</th>
-        [% END %]
-        </tr>
-
-        <tr valign="top">
-
-          [%# Can't use the select block here because of onChange and the fact that
-              'component' is a toolkit reserved word - we use 'component_' instead. %]
-          <td align="left">
-            <select name="product" multiple size="5" onChange="selectProduct(this.form);">
-            [% FOREACH p = product %]
-              <option value="[% p FILTER html %]"
-                [% " selected" IF lsearch(default.product, p) != -1 %]>
-                [% p FILTER html %]</option>
-            [% END %]
-            </select>
-          </td>
-
-          <td align="left">
-            <select name="component" multiple size="5">
-            [% FOREACH c = component_ %]
-              <option value="[% c FILTER html %]"
-                [% " selected" IF lsearch(default.component, c) != -1 %]>
-                [% c FILTER html %]</option>
-            [% END %]
-            </select>
-          </td>
-
-          [% PROCESS select sel = { name => 'version', size => 5 } %]
-
-        [% IF Param('usetargetmilestone') && target_milestone.size > 0 %]
-          [% PROCESS select sel = { name => 'target_milestone', size => 5 } %]
-        [% END %]
-        </tr>
-      </table>
-    </td>
-  </tr>
-  
-[%# *** Comment URL Whiteboard Keywords *** %]
-
-  [% FOREACH field = [ 
-    { name => "long_desc", description => "A&nbsp;comment" },
-    { name => "bug_file_loc", description => "The&nbsp;URL" },
-    { name => "status_whiteboard", description => "Whiteboard" } ] %]
-
-    [% UNLESS field.name == 'status_whiteboard' AND NOT Param('usestatuswhiteboard') %]
-    <tr>
-      <th align="right">[% field.description %]:</th>
-      <td>
-        <select name="[% field.name %]_type">
-        [% FOREACH qv = query_variants %]
-          [% type = "${field.name}_type" %]                   
-          <option value="[% qv.value %]"
-            [% " selected" IF default.$type.0 == qv.value %]>[% qv.description %]</option>
-        [% END %]              
-        </select>
-      </td>
-      <td><input name="[% field.name %]" size="40" value="
-        [% default.${field.name}.0 FILTER html %]">
-      </td>
-      <td></td>
-    </tr>  
-    [% END %]
-  [% END %]
-
-  [% IF have_keywords %]
-    <tr>
-      <th align="right"><a href="describekeywords.cgi">Keywords</a>:</th>
-      <td>
-        <select name="keywords_type">
-        [% FOREACH qv = [ 
-          { name => "anywords", description => "contains any of the keywords" },
-          { name => "allwords", description => "contains all of the keywords" },
-          { name => "nowords",  description => "contains none of the keywords" } ] %]
-
-          <option value="[% qv.name %]"
-            [% " selected" IF default.keywords_type.0 == qv.name %]>
-            [% qv.description %]</option>
-        [% END %]
-        </select>
-      </td>
-      <td>
-        <input name="keywords" size="40" value="[% default.keywords.0 FILTER html %]">
-      </td>
-    </tr>
-  [% END %]
-</table>  
+[% PROCESS search/knob.html.tmpl %]
 
 <hr>
 
-[%# *** Status Resolution Severity Priority Hardware OS *** %]
-
-<table>
-  <tr>
-    <th align="left"><a href="queryhelp.cgi#status">Status</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#resolution">Resolution</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#severity">Severity</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#priority">Priority</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#platform">Hardware</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#opsys">OS</a>:</th>
-  </tr>
-
-  <tr valign="top">
-    [% PROCESS select sel = { name => 'bug_status', size => 7 } %]
-    [% PROCESS select sel = { name => 'resolution', size => 7 } %]
-    [% PROCESS select sel = { name => 'bug_severity', size => 7 } %]    
-    [% PROCESS select sel = { name => 'priority', size => 7 } %]    
-    [% PROCESS select sel = { name => 'rep_platform', size => 7 } %]
-    [% PROCESS select sel = { name => 'op_sys', size => 7 } %]
-  </tr>
-</table>
+[% PROCESS "search/boolean-charts.html.tmpl" %]
 
 <p>
+  Give me a <a href="queryhelp.cgi">clue</a> about how to use this form.
+</p>
 
-[%# *** Email Numbering Votes *** %]
-
-<table>
-  <tr>
-    <td>
-      <fieldset>
-        <legend>
-          <strong>
-            <a href="queryhelp.cgi#peopleinvolved">Email</a> and Numbering
-          </strong>  
-        </legend>
-
-
-<table>
-  <tr>
-  [% FOREACH n = [1, 2] %]
-    <td>
-
-
-<table cellspacing="0" cellpadding="0">
-  <tr>
-    <td>
-      Any of:
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <input type="checkbox" name="emailassigned_to[% n %]" 
-             id="emailassigned_to[% n %]" value="1"
-             [% " checked" IF default.emailassigned_to.$n %]>
-      <label for="emailassigned_to[% n %]">
-        bug owner
-      </label>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <input type="checkbox" name="emailreporter[% n %]" 
-             id="emailreporter[% n %]" value="1"
-             [% " checked" IF default.emailreporter.$n %]>
-      <label for="emailreporter[% n %]">
-        reporter
-      </label>
-    </td>
-  </tr>
-  [% IF Param('useqacontact') %]
-  <tr>
-    <td>
-      <input type="checkbox" name="emailqa_contact[% n %]" 
-             id="emailqa_contact[% n %]" value="1"
-             [% " checked" IF default.emailqa_contact.$n %]>
-      <label for="emailqa_contact[% n %]">
-        QA contact
-      </label>
-    </td>
-  </tr>
-  [% END %]
-  <tr>
-    <td>
-      <input type="checkbox" name="emailcc[% n %]" 
-             id="emailcc[% n %]" value="1"
-             [% " checked" IF default.emailcc.$n %]>
-      <label for="emailcc[% n %]">
-        CC list member
-      </label>
-    </td>
-  </tr>
-  <tr>
-    <td>
-        <input type="checkbox" name="emaillongdesc[% n %]" 
-               id="emaillongdesc[% n %]" value="1"
-               [% " checked" IF default.emaillongdesc.$n %]>
-      <label for="emaillongdesc[% n %]">
-        commenter
-      </label>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <select name="emailtype[% n %]">
-      [% FOREACH qv = [ 
-        { name => "substring", description => "contains" },
-        { name => "exact", description => "is" },
-        { name => "regexp", description => "matches regexp" },
-        { name => "notregexp", description => "doesn&#8217;t match regexp" } ] %]
-        
-        <option value="[% qv.name %]"
-          [% " selected" IF default.emailtype.$n == qv.name %]>[% qv.description %]</option>
-      [% END %]
-      </select>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <input name="email[% n %]" size="20" value="[% default.email.$n FILTER html %]">
-    </td>
-  </tr>
-</table>
-
-    
-    </td>
-  [% END %]
-  </tr>
-</table>
-<hr>
-<table>  
-  <tr>
-    <td>
-      <select name="bugidtype">
-        <option value="include"[% " selected" IF default.bugidtype.0 == "include" %]>Only include</option>
-        <option value="exclude"[% " selected" IF default.bugidtype.0 == "exclude" %]>Exclude</option>
-      </select>
-      bugs numbered: 
-    </td>
-    <td>
-      <input type="text" name="bug_id" value="[% default.bug_id.0 FILTER html %]" size="20">
-    </td>
-  </tr>
-  <tr>
-    <td></td>
-    <td>(comma-separated list)</td>
-  </tr>
-  <tr>
-    <td align="right">
-      Only bugs with at least:
-    </td>
-    <td>
-      <input name="votes" size="3" value="[% default.votes.0 FILTER html %]"> votes
-    </td>
-  </tr>
-</table>
-
-
-      </fieldset>
-    </td>
-   
-[%# *** Bug Changes *** %]
-
-    <td valign="top">
-      <fieldset>
-        <legend><strong>Bug Changes</strong></legend>
-
-
-<dl>
-  <dt>Only bugs changed in the last </dt>
-  <dd><input name=changedin size=3 value="[% default.changedin.0 FILTER html %]"> days</dd>
-</dl>
-
-<dl>
-  <dt>Only bugs where any of the fields</dt>
-  <dd>
-    <select name="chfield" multiple size="4">
-    [% FOREACH field = chfield %]
-      <option value="[% field FILTER html %]"
-        [% " selected" IF lsearch(default.chfield, field) != -1 %]>
-        [% field FILTER html %]</option>
-    [% END %]
-    </select>
-  </dd>
-
-  <dt>were changed between</dt>
-  <dd>
-    <input name="chfieldfrom" size="10" value="[% default.chfieldfrom.0 FILTER html %]">
-    and <input name="chfieldto" size="10" value="[% default.chfieldto.0 FILTER html %]">
-    <br>(YYYY-MM-DD)
-  </dd>
-  <dt>to this value: (optional)</dt>
-  <dd>
-    <input name="chfieldvalue" size="20" value="[% default.chfieldvalue.0 FILTER html %]">
-  </dd>
-</dl>
-
-
-       </fieldset>
-     </td>
-  </tr>
-
-[%# *** Action Selection *** %]
-
-  <tr>
-    <td colspan="2">
-
-    [% IF NOT userid %]
-      <input type="hidden" name="cmdtype" value="doit">
-    [% ELSE %]
-      <br>
-      <input type="radio" name="cmdtype" value="doit" checked> Run this query
-      <br>
-
-      [% IF namedqueries.size > 0 %]
-        <p>
-        <table cellspacing="0" cellpadding="0">
-          <tr>
-           <td>
-             <input type="radio" name="cmdtype" value="editnamed">
-             Load my remembered query:
-           </td>
-           <td rowspan="3">
-             <select name="namedcmd">
-             [% FOREACH query = namedqueries %]
-               <option value="[% query FILTER html %]">[% query FILTER html %]</option>
-             [% END %]
-             </select>
-            </td>
-          </tr>  
-          <tr>
-            <td>
-              <input type="radio" name="cmdtype" value="runnamed">
-              Run my remembered query:
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <input type="radio" name="cmdtype" value="forgetnamed">
-              Forget my remembered query:
-            </td>
-          </tr>
-        </table>
-        </p>
-      [% END %]
-
-      <input type="radio" name="cmdtype" value="asdefault">
-      Remember this as my default query
-      <br>
-      <input type="radio" name="cmdtype" value="asnamed">
-      Remember this query, and name it:
-      <input type="text" name="newqueryname">
-      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="checkbox" name="tofooter" value="1">
-          and put it in my page footer
-      <br>
-    [% END %]
-      <br>
-      <div>
-      Sort results by:
-      <select name="order">
-      [% FOREACH order = orders %]
-        <option value="[% order FILTER html %]"
-          [% " selected" IF default.order.0 == order %]>[% order FILTER html %]</option> 
-      [% END %]
-      </select>
-
-      <input type="submit" value="Search">
-      [% IF userdefaultquery %]
-         <p>
-           <a href="query.cgi?nukedefaultquery=1">
-             Set my default query back to the system default</a>
-         </p>
-      [% END %]
-      </div>
-    </td>
-  </tr>
-</table>
-
-[%# *** Boolean Charts *** %]
-
-<hr>
-
-[% types = [
-  { name => "noop", description => "---" },
-  { name => "equals", description => "is equal to" },
-  { name => "notequals", description => "is not equal to" },
-  { name => "substring", description => "contains the string" },
-  { name => "casesubstring", description => "contains the string (exact case)" },
-  { name => "notsubstring", description => "does not contain the string" },
-  { name => "allwordssubstr", description => "contains all of the strings" },
-  { name => "anywordssubstr", description => "contains any of the strings" },
-  { name => "regexp", description => "contains regexp" },
-  { name => "notregexp", description => "does not contain regexp" },
-  { name => "lessthan", description => "is less than" },
-  { name => "greaterthan", description => "is greater than" },
-  { name => "anywords", description => "contains any of the words" },
-  { name => "allwords", description => "contains all of the words" },
-  { name => "nowords", description => "contains none of the words" },
-  { name => "changedbefore", description => "changed before" },
-  { name => "changedafter", description => "changed after" },
-  { name => "changedfrom", description => "changed from" },
-  { name => "changedto", description => "changed to" },
-  { name => "changedby", description => "changed by" } ] %]
-
-  <p>
-    <strong>
-      <a name="chart" href="queryhelp.cgi#advancedquerying">
-      Advanced Querying Using Boolean Charts</a>:
-    </strong>
-  </p>
-
-[%# Whoever wrote the original version of boolean charts had a seriously twisted mind %]
-
-[% jsmagic = "onclick=\"document.forms[0].action='query.cgi#chart'; document.forms[0].method='POST'; return 1;\"" %]
-
-[% FOREACH chart = default.charts %]
-  [% chartnum = loop.count - 1 %]
-  <table>
-  [% FOREACH row = chart %]
-    [% rownum = loop.count - 1 %]
-    <tr>
-    [% FOREACH col = row %]
-      [% colnum = loop.count - 1 %]
-      <td>
-        <select name="[% "field${chartnum}-${rownum}-${colnum}" %]">
-          [% FOREACH field = fields %]
-            <option value="[% field.name %]"
-              [%- " selected" IF field.name == col.field %]>[% field.description %]</option>
-          [% END %]
-        </select>
-
-        <select name="[% "type${chartnum}-${rownum}-${colnum}" %]">
-          [% FOREACH type = types %]
-            <option value="[% type.name %]"
-              [%- " selected" IF type.name == col.type %]>[% type.description %]</option>
-          [% END %]
-        </select>
-
-        <input name="[% "value${chartnum}-${rownum}-${colnum}" %]" 
-               value="[% col.value FILTER html %]"> 
-      </td>
-      
-      [% IF NOT col == row.last %]
-        <td align="center"> 
-          Or 
-        </td>    
-      [% ELSE %]
-        <td>
-          [% newor = colnum + 1 %]
-          <input type="submit" value="Or" 
-                 name="cmd-add[% "${chartnum}-${rownum}-${newor}" %]" [% $jsmagic %]>
-        </td>
-      [% END %]
-      
-    [% END %]
-    </tr>
-    
-    [% IF NOT row == chart.last %]
-    <tr>
-      <td>And</td>
-    </tr>    
-    [% ELSE %]
-    <tr>
-      <td>
-        [% newand = rownum + 1; newchart = chartnum + 1 %]
-        <input type="submit" value="And" 
-               name="cmd-add[% "${chartnum}-${newand}-0" %]"[% $jsmagic %]> 
-        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-        <input type="submit" value="Add another boolean chart" 
-               name="cmd-add[% newchart %]-0-0" [% $jsmagic %]>
-        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-      </td>
-    </tr>   
-    [% END %]
-    
-  [% END %]
-  </table>
-  <hr>
-[% END %]
-
-<p>Give me a <a href="queryhelp.cgi">clue</a> about how to use this form.</p>
-
-</FORM>
+</form>
 
 [% PROCESS global/footer.html.tmpl %]
-
-[%############################################################################%]
-[%# Block for SELECT fields                                                  #%]
-[%############################################################################%]
-
-[% BLOCK select %]
-  <td align="left">
-    <select name="[% sel.name %]" multiple size="[% sel.size %]">
-    [% FOREACH name = ${sel.name} %]
-      <option value="[% name FILTER html %]"
-        [% " selected" IF lsearch(default.${sel.name}, name) != -1 %]>
-        [% name FILTER html %]</option>
-    [% END %]
-    </select>
-  </td>
-[% END %]
index ec93e85924d503b37bba08232d0dbc71477631ca..b64470937d7c3783b8ad06cfa35a77adc839c4bf 100644 (file)
@@ -16,9 +16,7 @@
   # Copyright (C) 1998 Netscape Communications Corporation. All
   # Rights Reserved.
   #
-  # Contributor(s): Chris Lahey <clahey@ximian.com> [javascript fixes]
-  #                 Christian Reis <kiko@async.com.br> [javascript rewrite]
-  #                 Gervase Markham <gerv@gerv.net>
+  # Contributor(s): Gervase Markham <gerv@gerv.net>
   #%]
 
 [% PROCESS global/header.html.tmpl 
   extra = " onLoad=\"selectProduct(document.forms['queryform']);\""
 %]
 
-[%# Note: use Template comments and not JS ones here, to avoid bloating
-    what we actually send to the browser %]
+[% button_name = "Search" %]
 
-<script language="JavaScript" type="text/javascript"> <!--
+[% PROCESS search/form.html.tmpl %]
 
-var first_load = true;         [%# is this the first time we load the page? %]
-var last_sel = new Array();    [%# caches last selection %]
-
-var cpts = new Array();
-var vers = new Array();
-[% IF Param('usetargetmilestone') %]
-var tms = new Array();
-[% END %]
-
-[%# Create three arrays of components, versions and target milestones, indexed 
-  # numerically according to the product they refer to. #%]
-
-[% n = 0 %]
-[% FOREACH p = product %]
-  cpts[[% n %]] = [ 
-    [%- FOREACH item = componentsbyproduct.$p %]'[% item FILTER js %]', [%- END -%]];
-  vers[[% n %]] = [ 
-    [%- FOREACH item = versionsbyproduct.$p -%]'[%  item FILTER js %]', [%- END -%]];
-  [% IF Param('usetargetmilestone') %]
-  tms[[% n %]]  = [ 
-     [%- FOREACH item = milestonesbyproduct.$p %]'[% item FILTER js %]', [%- END -%]];
-  [% END %]
-  [% n = n+1 %]
-[% END %]
-
-[%# updateSelect(array, sel, target, merging)
-  #
-  # Adds to the target select object all elements in array that
-  # correspond to the elements selected in source.
-  # - array should be a array of arrays, indexed by number. the
-  #   array should contain the elements that correspond to that
-  #   product. 
-  # - sel is a list of selected items, either whole or a diff
-  #   depending on merging.
-  # - target should be the target select object.
-  # - merging (boolean) determines if we are mergine in a diff or
-  #   substituting the whole selection. a diff is used to optimize adding
-  #   selections.
-  #
-  # Example (compsel is a select form control)
-  #
-  #     var components = Array();
-  #     components[1] = [ 'ComponentA', 'ComponentB' ];
-  #     components[2] = [ 'ComponentC', 'ComponentD' ];
-  #     source = [ 2 ];
-  #     updateSelect(components, source, compsel, 0, 0);
-  #
-  # would clear compsel and add 'ComponentC' and 'ComponentD' to it.
-  #
-  %]
-
-function updateSelect(array, sel, target, merging) {
-        
-    var i, item;
-
-    [%# If we have no versions/components/milestones %]
-    if (array.length < 1) {
-        target.options.length = 0;
-        return false;
-    }
-
-    if (merging) {
-        [%# array merging/sorting in the case of multiple selections %]
-        [%# merge in the current options with the first selection %]
-        item = merge_arrays(array[sel[0]], target.options, 1);
-
-        [%# merge the rest of the selection with the results %]
-        for (i = 1 ; i < sel.length ; i++) {
-            item = merge_arrays(array[sel[i]], item, 0);
-        }
-    } else if ( sel.length > 1 ) {
-        [%# here we micro-optimize for two arrays to avoid merging with a
-            null array %]
-        item = merge_arrays(array[sel[0]],array[sel[1]], 0);
-
-        [%# merge the arrays. not very good for multiple selections. %]
-        for (i = 2; i < sel.length; i++) {
-            item = merge_arrays(item, array[sel[i]], 0);
-        }
-    } else { [%# single item in selection, just get me the list %]
-        item = array[sel[0]];
-    }
-
-    [%# clear select %]
-    target.options.length = 0;
-
-    [%# load elements of list into select %]
-    for (i = 0; i < item.length; i++) {
-        target.options[i] = new Option(item[i], item[i]);
-    }
-    return true;
-}
-
-[%# Returns elements in a that are not in b. 
-  # NOT A REAL DIFF: does not check the reverse.
-  #    - a,b: arrays of values to be compare. %]
-function fake_diff_array(a, b) {
-    var newsel = new Array();
-    var found = false;
-
-    [%# do a boring array diff to see who's new %]
-    for (var ia in a) {
-        for (var ib in b) {
-            if (a[ia] == b[ib]) {
-                found = true;
-            }
-        }
-        if (!found) {
-            newsel[newsel.length] = a[ia];
-        }
-        found = false;
-    }
-    return newsel;
-}
-
-[%# takes two arrays and sorts them by string, returning a new, sorted
-  # array. the merge removes dupes, too.
-  #    - a, b: arrays to be merge.
-  #    - b_is_select: if true, then b is actually an optionitem and as
-  #      such we need to use item.value on it. %]
-function merge_arrays(a, b, b_is_select) {
-    var pos_a = 0;
-    var pos_b = 0;
-    var ret = new Array();
-    var bitem, aitem;
-
-    [%# iterate through both arrays and add the larger item to the return
-       list. remove dupes, too. Use toLowerCase to provide
-       case-insensitivity. %]
-    while ((pos_a < a.length) && (pos_b < b.length)) {
-        if (b_is_select) {
-            bitem = b[pos_b].value;
-        } else {
-            bitem = b[pos_b];
-        }
-        aitem = a[pos_a];
-
-        [%# smaller item in list a %]
-        if (aitem.toLowerCase() < bitem.toLowerCase()) {
-            ret[ret.length] = aitem;
-            pos_a++;
-        } else {
-            [%# smaller item in list b %]
-            if (aitem.toLowerCase() > bitem.toLowerCase()) {
-                ret[ret.length] = bitem;
-                pos_b++;
-            } else {
-                [%# list contents are equal, inc both counters. %]
-                ret[ret.length] = aitem;
-                pos_a++;
-                pos_b++;
-            }
-        }
-    }
-
-    [%# catch leftovers here. these sections are ugly code-copying. %]
-    if (pos_a < a.length) {
-        for (; pos_a < a.length ; pos_a++) {
-            ret[ret.length] = a[pos_a];
-        }
-    }
-
-    if (pos_b < b.length) {
-        for (; pos_b < b.length; pos_b++) {
-            if (b_is_select) {
-                bitem = b[pos_b].value;
-            } else {
-                bitem = b[pos_b];
-            }
-            ret[ret.length] = bitem;
-        }
-    }
-    return ret;
-}
-
-[%# Returns an array of indexes or values from a select form control.
-  #    - control: select control from which to find selections
-  #    - findall: boolean, store all options when true or just the selected 
-  #      indexes
-  #    - want_values: boolean; we store values when true and indexes when
-  #      false %]
-function getSelection(control, findall, want_values) {
-    var ret = new Array();
-
-    if ((!findall) && (control.selectedIndex == -1)) {
-        return ret;
-    }
-
-    for (var i=0; i<control.length; i++) {
-        if (findall || control.options[i].selected) {
-            ret[ret.length] = want_values ? control.options[i].value : i;
-        }
-    }
-    return ret;
-}
-
-[%# Selects items in control that have index defined in sel
-  #    - control: SELECT control to be restored
-  #    - selnames: array of indexes in select form control %]
-function restoreSelection(control, selnames) {
-    [%# right. this sucks. but I see no way to avoid going through the
-      # list and comparing to the contents of the control. %]
-    for (var j=0; j < selnames.length; j++) {
-        for (var i=0; i < control.options.length; i++) {
-            if (control.options[i].value == selnames[j]) {
-                control.options[i].selected = true;
-            }
-        }
-    }
-}
-
-[%# selectProduct reads the selection from f.product and updates
-  # f.version, component and target_milestone accordingly.
-  #     - f: a form containing product, component, varsion and
-  #       target_milestone select boxes.
-  # globals (3vil!):
-  #     - cpts, vers, tms: array of arrays, indexed by product name. the
-  #       subarrays contain a list of names to be fed to the respective
-  #       selectboxes. For bugzilla, these are generated with perl code
-  #       at page start.
-  #     - first_load: boolean, specifying if it is the first time we load
-  #       the query page.
-  #     - last_sel: saves our last selection list so we know what has
-  #       changed, and optimize for additions. %]
-function selectProduct(f) {
-    [%# this is to avoid handling events that occur before the form
-        itself is ready, which could happen in buggy browsers. %]
-    if ((!f) || (!f.product)) {
-        return;
-    }
-
-    [%# if this is the first load and nothing is selected, no need to
-        merge and sort all components; perl gives it to us sorted. %]
-    if ((first_load) && (f.product.selectedIndex == -1)) {
-        first_load = false;
-        return;
-    }
-    
-    [%# turn first_load off. this is tricky, since it seems to be
-        redundant with the above clause. It's not: if when we first load
-        the page there is _one_ element selected, it won't fall into that
-        clause, and first_load will remain 1. Then, if we unselect that
-        item, selectProduct will be called but the clause will be valid
-        (since selectedIndex == -1), and we will return - incorrectly -
-        without merge/sorting. %]
-    first_load = false;
-
-    [%# - sel keeps the array of products we are selected. 
-        - merging says if it is a full list or just a list of products that
-          were added to the current selection. %]
-    var merging = false;
-    var sel = Array();
-
-    [%# if nothing selected, pick all %]
-    var findall = f.product.selectedIndex == -1;
-    sel = getSelection(f.product, findall, false);
-    if (!findall) {
-        [%# save sel for the next invocation of selectProduct() %]
-        var tmp = sel;
-    
-        [%# this is an optimization: if we have just added products to an
-            existing selection, no need to clear the form controls and add 
-            everybody again; just merge the new ones with the existing 
-            options. %]
-        if ((last_sel.length > 0) && (last_sel.length < sel.length)) {
-            sel = fake_diff_array(sel, last_sel);
-            merging = true;
-        }
-        last_sel = tmp;
-    }
-    [%# save original options selected %]
-    var saved_cpts = getSelection(f.component, false, true);
-    var saved_vers = getSelection(f.version, false, true);
-    [% IF Param('usetargetmilestone') %]
-    var saved_tms = getSelection(f.target_milestone, false, true);
-    [% END %]
-
-    [%# do the actual fill/update, reselect originally selected options %]
-    updateSelect(cpts, sel, f.component, merging);
-    restoreSelection(f.component, saved_cpts);
-    updateSelect(vers, sel, f.version, merging);
-    restoreSelection(f.version, saved_vers);
-    [% IF Param('usetargetmilestone') %]
-    updateSelect(tms, sel, f.target_milestone, merging);
-    restoreSelection(f.target_milestone, saved_tms);
-    [% END %]
-}
-
-// -->
-</script>
-
-[% query_variants = [ 
-  { value => "allwordssubstr", description => "contains all of the words/strings" },
-  { value => "anywordssubstr", description => "contains any of the words/strings" },
-  { value => "substring", description => "contains the string" },
-  { value => "casesubstring", description => "contains the string (exact case)" },
-  { value => "allwords", description => "contains all of the words" },
-  { value => "anywords", description => "contains any of the words" },
-  { value => "regexp", description => "matches the regexp" },
-  { value => "notregexp", description => "doesn&#8217;t match the regexp" } ] %]
-     
-<form method="get" action="buglist.cgi" name="queryform">
-
-[%# *** Summary *** %]
-
-<table>
-  <tr>
-    <th align="right">Summary:</th>
-    <td>
-      <select name="short_desc_type">
-      [% FOREACH qv = query_variants %]
-        <option value="[% qv.value %]"
-          [% " selected" IF default.short_desc_type.0 == qv.value %]>[% qv.description %]</option>
-      [% END %]              
-      </select>
-    </td>
-    <td>
-      <input name="short_desc" size="40" value="[% default.short_desc.0 FILTER html %]">
-    </td>
-    <td>
-      <input type="submit" value="Search">
-    </td>
-  </tr>  
-
-[%# *** Product Component Version Target *** %]
-
-  <tr>
-    <td colspan="4">
-      <table>
-        <tr valign="bottom">
-          <th align="left">Product:</th>
-          <th align="left"><a href="describecomponents.cgi">Component</a>:</th>
-          <th align="left">Version:</th>
-
-        [% IF (Param("usetargetmilestone")) %]
-          <th align="left">Target:</th>
-        [% END %]
-        </tr>
-
-        <tr valign="top">
-
-          [%# Can't use the select block here because of onChange and the fact that
-              'component' is a toolkit reserved word - we use 'component_' instead. %]
-          <td align="left">
-            <select name="product" multiple size="5" onChange="selectProduct(this.form);">
-            [% FOREACH p = product %]
-              <option value="[% p FILTER html %]"
-                [% " selected" IF lsearch(default.product, p) != -1 %]>
-                [% p FILTER html %]</option>
-            [% END %]
-            </select>
-          </td>
-
-          <td align="left">
-            <select name="component" multiple size="5">
-            [% FOREACH c = component_ %]
-              <option value="[% c FILTER html %]"
-                [% " selected" IF lsearch(default.component, c) != -1 %]>
-                [% c FILTER html %]</option>
-            [% END %]
-            </select>
-          </td>
-
-          [% PROCESS select sel = { name => 'version', size => 5 } %]
-
-        [% IF Param('usetargetmilestone') && target_milestone.size > 0 %]
-          [% PROCESS select sel = { name => 'target_milestone', size => 5 } %]
-        [% END %]
-        </tr>
-      </table>
-    </td>
-  </tr>
-  
-[%# *** Comment URL Whiteboard Keywords *** %]
-
-  [% FOREACH field = [ 
-    { name => "long_desc", description => "A&nbsp;comment" },
-    { name => "bug_file_loc", description => "The&nbsp;URL" },
-    { name => "status_whiteboard", description => "Whiteboard" } ] %]
-
-    [% UNLESS field.name == 'status_whiteboard' AND NOT Param('usestatuswhiteboard') %]
-    <tr>
-      <th align="right">[% field.description %]:</th>
-      <td>
-        <select name="[% field.name %]_type">
-        [% FOREACH qv = query_variants %]
-          [% type = "${field.name}_type" %]                   
-          <option value="[% qv.value %]"
-            [% " selected" IF default.$type.0 == qv.value %]>[% qv.description %]</option>
-        [% END %]              
-        </select>
-      </td>
-      <td><input name="[% field.name %]" size="40" value="
-        [% default.${field.name}.0 FILTER html %]">
-      </td>
-      <td></td>
-    </tr>  
-    [% END %]
-  [% END %]
-
-  [% IF have_keywords %]
-    <tr>
-      <th align="right"><a href="describekeywords.cgi">Keywords</a>:</th>
-      <td>
-        <select name="keywords_type">
-        [% FOREACH qv = [ 
-          { name => "anywords", description => "contains any of the keywords" },
-          { name => "allwords", description => "contains all of the keywords" },
-          { name => "nowords",  description => "contains none of the keywords" } ] %]
-
-          <option value="[% qv.name %]"
-            [% " selected" IF default.keywords_type.0 == qv.name %]>
-            [% qv.description %]</option>
-        [% END %]
-        </select>
-      </td>
-      <td>
-        <input name="keywords" size="40" value="[% default.keywords.0 FILTER html %]">
-      </td>
-    </tr>
-  [% END %]
-</table>  
+[% PROCESS search/knob.html.tmpl %]
 
 <hr>
 
-[%# *** Status Resolution Severity Priority Hardware OS *** %]
-
-<table>
-  <tr>
-    <th align="left"><a href="queryhelp.cgi#status">Status</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#resolution">Resolution</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#severity">Severity</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#priority">Priority</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#platform">Hardware</a>:</th>
-    <th align="left"><a href="queryhelp.cgi#opsys">OS</a>:</th>
-  </tr>
-
-  <tr valign="top">
-    [% PROCESS select sel = { name => 'bug_status', size => 7 } %]
-    [% PROCESS select sel = { name => 'resolution', size => 7 } %]
-    [% PROCESS select sel = { name => 'bug_severity', size => 7 } %]    
-    [% PROCESS select sel = { name => 'priority', size => 7 } %]    
-    [% PROCESS select sel = { name => 'rep_platform', size => 7 } %]
-    [% PROCESS select sel = { name => 'op_sys', size => 7 } %]
-  </tr>
-</table>
+[% PROCESS "search/boolean-charts.html.tmpl" %]
 
 <p>
+  Give me a <a href="queryhelp.cgi">clue</a> about how to use this form.
+</p>
 
-[%# *** Email Numbering Votes *** %]
-
-<table>
-  <tr>
-    <td>
-      <fieldset>
-        <legend>
-          <strong>
-            <a href="queryhelp.cgi#peopleinvolved">Email</a> and Numbering
-          </strong>  
-        </legend>
-
-
-<table>
-  <tr>
-  [% FOREACH n = [1, 2] %]
-    <td>
-
-
-<table cellspacing="0" cellpadding="0">
-  <tr>
-    <td>
-      Any of:
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <input type="checkbox" name="emailassigned_to[% n %]" 
-             id="emailassigned_to[% n %]" value="1"
-             [% " checked" IF default.emailassigned_to.$n %]>
-      <label for="emailassigned_to[% n %]">
-        bug owner
-      </label>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <input type="checkbox" name="emailreporter[% n %]" 
-             id="emailreporter[% n %]" value="1"
-             [% " checked" IF default.emailreporter.$n %]>
-      <label for="emailreporter[% n %]">
-        reporter
-      </label>
-    </td>
-  </tr>
-  [% IF Param('useqacontact') %]
-  <tr>
-    <td>
-      <input type="checkbox" name="emailqa_contact[% n %]" 
-             id="emailqa_contact[% n %]" value="1"
-             [% " checked" IF default.emailqa_contact.$n %]>
-      <label for="emailqa_contact[% n %]">
-        QA contact
-      </label>
-    </td>
-  </tr>
-  [% END %]
-  <tr>
-    <td>
-      <input type="checkbox" name="emailcc[% n %]" 
-             id="emailcc[% n %]" value="1"
-             [% " checked" IF default.emailcc.$n %]>
-      <label for="emailcc[% n %]">
-        CC list member
-      </label>
-    </td>
-  </tr>
-  <tr>
-    <td>
-        <input type="checkbox" name="emaillongdesc[% n %]" 
-               id="emaillongdesc[% n %]" value="1"
-               [% " checked" IF default.emaillongdesc.$n %]>
-      <label for="emaillongdesc[% n %]">
-        commenter
-      </label>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <select name="emailtype[% n %]">
-      [% FOREACH qv = [ 
-        { name => "substring", description => "contains" },
-        { name => "exact", description => "is" },
-        { name => "regexp", description => "matches regexp" },
-        { name => "notregexp", description => "doesn&#8217;t match regexp" } ] %]
-        
-        <option value="[% qv.name %]"
-          [% " selected" IF default.emailtype.$n == qv.name %]>[% qv.description %]</option>
-      [% END %]
-      </select>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <input name="email[% n %]" size="20" value="[% default.email.$n FILTER html %]">
-    </td>
-  </tr>
-</table>
-
-    
-    </td>
-  [% END %]
-  </tr>
-</table>
-<hr>
-<table>  
-  <tr>
-    <td>
-      <select name="bugidtype">
-        <option value="include"[% " selected" IF default.bugidtype.0 == "include" %]>Only include</option>
-        <option value="exclude"[% " selected" IF default.bugidtype.0 == "exclude" %]>Exclude</option>
-      </select>
-      bugs numbered: 
-    </td>
-    <td>
-      <input type="text" name="bug_id" value="[% default.bug_id.0 FILTER html %]" size="20">
-    </td>
-  </tr>
-  <tr>
-    <td></td>
-    <td>(comma-separated list)</td>
-  </tr>
-  <tr>
-    <td align="right">
-      Only bugs with at least:
-    </td>
-    <td>
-      <input name="votes" size="3" value="[% default.votes.0 FILTER html %]"> votes
-    </td>
-  </tr>
-</table>
-
-
-      </fieldset>
-    </td>
-   
-[%# *** Bug Changes *** %]
-
-    <td valign="top">
-      <fieldset>
-        <legend><strong>Bug Changes</strong></legend>
-
-
-<dl>
-  <dt>Only bugs changed in the last </dt>
-  <dd><input name=changedin size=3 value="[% default.changedin.0 FILTER html %]"> days</dd>
-</dl>
-
-<dl>
-  <dt>Only bugs where any of the fields</dt>
-  <dd>
-    <select name="chfield" multiple size="4">
-    [% FOREACH field = chfield %]
-      <option value="[% field FILTER html %]"
-        [% " selected" IF lsearch(default.chfield, field) != -1 %]>
-        [% field FILTER html %]</option>
-    [% END %]
-    </select>
-  </dd>
-
-  <dt>were changed between</dt>
-  <dd>
-    <input name="chfieldfrom" size="10" value="[% default.chfieldfrom.0 FILTER html %]">
-    and <input name="chfieldto" size="10" value="[% default.chfieldto.0 FILTER html %]">
-    <br>(YYYY-MM-DD)
-  </dd>
-  <dt>to this value: (optional)</dt>
-  <dd>
-    <input name="chfieldvalue" size="20" value="[% default.chfieldvalue.0 FILTER html %]">
-  </dd>
-</dl>
-
-
-       </fieldset>
-     </td>
-  </tr>
-
-[%# *** Action Selection *** %]
-
-  <tr>
-    <td colspan="2">
-
-    [% IF NOT userid %]
-      <input type="hidden" name="cmdtype" value="doit">
-    [% ELSE %]
-      <br>
-      <input type="radio" name="cmdtype" value="doit" checked> Run this query
-      <br>
-
-      [% IF namedqueries.size > 0 %]
-        <p>
-        <table cellspacing="0" cellpadding="0">
-          <tr>
-           <td>
-             <input type="radio" name="cmdtype" value="editnamed">
-             Load my remembered query:
-           </td>
-           <td rowspan="3">
-             <select name="namedcmd">
-             [% FOREACH query = namedqueries %]
-               <option value="[% query FILTER html %]">[% query FILTER html %]</option>
-             [% END %]
-             </select>
-            </td>
-          </tr>  
-          <tr>
-            <td>
-              <input type="radio" name="cmdtype" value="runnamed">
-              Run my remembered query:
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <input type="radio" name="cmdtype" value="forgetnamed">
-              Forget my remembered query:
-            </td>
-          </tr>
-        </table>
-        </p>
-      [% END %]
-
-      <input type="radio" name="cmdtype" value="asdefault">
-      Remember this as my default query
-      <br>
-      <input type="radio" name="cmdtype" value="asnamed">
-      Remember this query, and name it:
-      <input type="text" name="newqueryname">
-      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="checkbox" name="tofooter" value="1">
-          and put it in my page footer
-      <br>
-    [% END %]
-      <br>
-      <div>
-      Sort results by:
-      <select name="order">
-      [% FOREACH order = orders %]
-        <option value="[% order FILTER html %]"
-          [% " selected" IF default.order.0 == order %]>[% order FILTER html %]</option> 
-      [% END %]
-      </select>
-
-      <input type="submit" value="Search">
-      [% IF userdefaultquery %]
-         <p>
-           <a href="query.cgi?nukedefaultquery=1">
-             Set my default query back to the system default</a>
-         </p>
-      [% END %]
-      </div>
-    </td>
-  </tr>
-</table>
-
-[%# *** Boolean Charts *** %]
-
-<hr>
-
-[% types = [
-  { name => "noop", description => "---" },
-  { name => "equals", description => "is equal to" },
-  { name => "notequals", description => "is not equal to" },
-  { name => "substring", description => "contains the string" },
-  { name => "casesubstring", description => "contains the string (exact case)" },
-  { name => "notsubstring", description => "does not contain the string" },
-  { name => "allwordssubstr", description => "contains all of the strings" },
-  { name => "anywordssubstr", description => "contains any of the strings" },
-  { name => "regexp", description => "contains regexp" },
-  { name => "notregexp", description => "does not contain regexp" },
-  { name => "lessthan", description => "is less than" },
-  { name => "greaterthan", description => "is greater than" },
-  { name => "anywords", description => "contains any of the words" },
-  { name => "allwords", description => "contains all of the words" },
-  { name => "nowords", description => "contains none of the words" },
-  { name => "changedbefore", description => "changed before" },
-  { name => "changedafter", description => "changed after" },
-  { name => "changedfrom", description => "changed from" },
-  { name => "changedto", description => "changed to" },
-  { name => "changedby", description => "changed by" } ] %]
-
-  <p>
-    <strong>
-      <a name="chart" href="queryhelp.cgi#advancedquerying">
-      Advanced Querying Using Boolean Charts</a>:
-    </strong>
-  </p>
-
-[%# Whoever wrote the original version of boolean charts had a seriously twisted mind %]
-
-[% jsmagic = "onclick=\"document.forms[0].action='query.cgi#chart'; document.forms[0].method='POST'; return 1;\"" %]
-
-[% FOREACH chart = default.charts %]
-  [% chartnum = loop.count - 1 %]
-  <table>
-  [% FOREACH row = chart %]
-    [% rownum = loop.count - 1 %]
-    <tr>
-    [% FOREACH col = row %]
-      [% colnum = loop.count - 1 %]
-      <td>
-        <select name="[% "field${chartnum}-${rownum}-${colnum}" %]">
-          [% FOREACH field = fields %]
-            <option value="[% field.name %]"
-              [%- " selected" IF field.name == col.field %]>[% field.description %]</option>
-          [% END %]
-        </select>
-
-        <select name="[% "type${chartnum}-${rownum}-${colnum}" %]">
-          [% FOREACH type = types %]
-            <option value="[% type.name %]"
-              [%- " selected" IF type.name == col.type %]>[% type.description %]</option>
-          [% END %]
-        </select>
-
-        <input name="[% "value${chartnum}-${rownum}-${colnum}" %]" 
-               value="[% col.value FILTER html %]"> 
-      </td>
-      
-      [% IF NOT col == row.last %]
-        <td align="center"> 
-          Or 
-        </td>    
-      [% ELSE %]
-        <td>
-          [% newor = colnum + 1 %]
-          <input type="submit" value="Or" 
-                 name="cmd-add[% "${chartnum}-${rownum}-${newor}" %]" [% $jsmagic %]>
-        </td>
-      [% END %]
-      
-    [% END %]
-    </tr>
-    
-    [% IF NOT row == chart.last %]
-    <tr>
-      <td>And</td>
-    </tr>    
-    [% ELSE %]
-    <tr>
-      <td>
-        [% newand = rownum + 1; newchart = chartnum + 1 %]
-        <input type="submit" value="And" 
-               name="cmd-add[% "${chartnum}-${newand}-0" %]"[% $jsmagic %]> 
-        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-        <input type="submit" value="Add another boolean chart" 
-               name="cmd-add[% newchart %]-0-0" [% $jsmagic %]>
-        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-      </td>
-    </tr>   
-    [% END %]
-    
-  [% END %]
-  </table>
-  <hr>
-[% END %]
-
-<p>Give me a <a href="queryhelp.cgi">clue</a> about how to use this form.</p>
-
-</FORM>
+</form>
 
 [% PROCESS global/footer.html.tmpl %]
-
-[%############################################################################%]
-[%# Block for SELECT fields                                                  #%]
-[%############################################################################%]
-
-[% BLOCK select %]
-  <td align="left">
-    <select name="[% sel.name %]" multiple size="[% sel.size %]">
-    [% FOREACH name = ${sel.name} %]
-      <option value="[% name FILTER html %]"
-        [% " selected" IF lsearch(default.${sel.name}, name) != -1 %]>
-        [% name FILTER html %]</option>
-    [% END %]
-    </select>
-  </td>
-[% END %]