Commit bf3e63a7 authored by Reed Loden's avatar Reed Loden

Bug 455810 - Add autocomplete support to the keywords field

* Special thanks to Guy Pyrzak for the original patch [r=mkanat a=mkanat]
parent e598bc84
...@@ -128,6 +128,7 @@ use File::Basename; ...@@ -128,6 +128,7 @@ use File::Basename;
FIELD_TYPE_DATETIME FIELD_TYPE_DATETIME
FIELD_TYPE_BUG_ID FIELD_TYPE_BUG_ID
FIELD_TYPE_BUG_URLS FIELD_TYPE_BUG_URLS
FIELD_TYPE_KEYWORDS
EMPTY_DATETIME_REGEX EMPTY_DATETIME_REGEX
...@@ -395,6 +396,7 @@ use constant FIELD_TYPE_TEXTAREA => 4; ...@@ -395,6 +396,7 @@ use constant FIELD_TYPE_TEXTAREA => 4;
use constant FIELD_TYPE_DATETIME => 5; use constant FIELD_TYPE_DATETIME => 5;
use constant FIELD_TYPE_BUG_ID => 6; use constant FIELD_TYPE_BUG_ID => 6;
use constant FIELD_TYPE_BUG_URLS => 7; use constant FIELD_TYPE_BUG_URLS => 7;
use constant FIELD_TYPE_KEYWORDS => 8;
use constant EMPTY_DATETIME_REGEX => qr/^[0\-:\sA-Za-z]+$/; use constant EMPTY_DATETIME_REGEX => qr/^[0\-:\sA-Za-z]+$/;
......
...@@ -182,7 +182,7 @@ use constant DEFAULT_FIELDS => ( ...@@ -182,7 +182,7 @@ use constant DEFAULT_FIELDS => (
{name => 'status_whiteboard', desc => 'Status Whiteboard', {name => 'status_whiteboard', desc => 'Status Whiteboard',
in_new_bugmail => 1, buglist => 1}, in_new_bugmail => 1, buglist => 1},
{name => 'keywords', desc => 'Keywords', in_new_bugmail => 1, {name => 'keywords', desc => 'Keywords', in_new_bugmail => 1,
buglist => 1}, type => FIELD_TYPE_KEYWORDS, buglist => 1},
{name => 'resolution', desc => 'Resolution', {name => 'resolution', desc => 'Resolution',
type => FIELD_TYPE_SINGLE_SELECT, buglist => 1}, type => FIELD_TYPE_SINGLE_SELECT, buglist => 1},
{name => 'bug_severity', desc => 'Severity', in_new_bugmail => 1, {name => 'bug_severity', desc => 'Severity', in_new_bugmail => 1,
...@@ -322,7 +322,7 @@ sub _check_type { ...@@ -322,7 +322,7 @@ sub _check_type {
my $saved_type = $type; my $saved_type = $type;
# The constant here should be updated every time a new, # The constant here should be updated every time a new,
# higher field type is added. # higher field type is added.
(detaint_natural($type) && $type <= FIELD_TYPE_BUG_URLS) (detaint_natural($type) && $type <= FIELD_TYPE_KEYWORDS)
|| ThrowCodeError('invalid_customfield_type', { type => $saved_type }); || ThrowCodeError('invalid_customfield_type', { type => $saved_type });
my $custom = blessed($invocant) ? $invocant->custom : $params->{custom}; my $custom = blessed($invocant) ? $invocant->custom : $params->{custom};
......
...@@ -765,6 +765,9 @@ sub create { ...@@ -765,6 +765,9 @@ sub create {
# Whether or not keywords are enabled, in this Bugzilla. # Whether or not keywords are enabled, in this Bugzilla.
'use_keywords' => sub { return Bugzilla::Keyword->any_exist; }, 'use_keywords' => sub { return Bugzilla::Keyword->any_exist; },
# All the keywords.
'all_keywords' => sub { return Bugzilla::Keyword->get_all(); },
'feature_enabled' => sub { return Bugzilla->feature(@_); }, 'feature_enabled' => sub { return Bugzilla->feature(@_); },
# field_descs can be somewhat slow to generate, so we generate # field_descs can be somewhat slow to generate, so we generate
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
* *
* Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> * Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
* Guy Pyrzak <guy.pyrzak@gmail.com> * Guy Pyrzak <guy.pyrzak@gmail.com>
* Reed Loden <reed@reedloden.com>
*/ */
/* This library assumes that the needed YUI libraries have been loaded /* This library assumes that the needed YUI libraries have been loaded
...@@ -621,8 +622,8 @@ YAHOO.bugzilla.userAutocomplete = { ...@@ -621,8 +622,8 @@ YAHOO.bugzilla.userAutocomplete = {
userAutoComp.autoHighlight = false; userAutoComp.autoHighlight = false;
// this is a throttle to determine the delay of the query from typing // this is a throttle to determine the delay of the query from typing
// set this higher to cause fewer calls to the server // set this higher to cause fewer calls to the server
userAutoComp.queryDelay = 0.05 userAutoComp.queryDelay = 0.05;
userAutoComp.useIFrame = true userAutoComp.useIFrame = true;
userAutoComp.resultTypeList = false; userAutoComp.resultTypeList = false;
if( multiple == true ){ if( multiple == true ){
userAutoComp.delimChar = [","," "]; userAutoComp.delimChar = [","," "];
...@@ -631,3 +632,32 @@ YAHOO.bugzilla.userAutocomplete = { ...@@ -631,3 +632,32 @@ YAHOO.bugzilla.userAutocomplete = {
} }
}; };
YAHOO.bugzilla.keywordAutocomplete = {
dataSource : null,
init_ds : function(){
this.dataSource = new YAHOO.util.LocalDataSource( YAHOO.bugzilla.keyword_array );
},
init : function( field, container ) {
if( this.dataSource == null ){
this.init_ds();
}
var keywordAutoComp = new YAHOO.widget.AutoComplete(field, container, this.dataSource);
keywordAutoComp.maxResultsDisplayed = YAHOO.bugzilla.keyword_array.length;
keywordAutoComp.minQueryLength = 0;
keywordAutoComp.useIFrame = true;
keywordAutoComp.delimChar = [","," "];
keywordAutoComp.resultTypeList = false;
keywordAutoComp.queryDelay = 0;
/* Causes all the possibilities in the keyword to appear when a user
* focuses on the textbox
*/
keywordAutoComp.textboxFocusEvent.subscribe( function(){
var sInputValue = YAHOO.util.Dom.get('keywords').value;
if( sInputValue.length === 0 ){
this.sendQuery(sInputValue);
this.collapseContainer();
this.expandContainer();
}
});
}
};
...@@ -47,6 +47,10 @@ form#Create #comp_desc { ...@@ -47,6 +47,10 @@ form#Create #comp_desc {
#bug_id_container, .search_field_grid, #bug_id_container, .search_field_grid,
.search_email_fields, ul.bug_changes li { .search_email_fields, ul.bug_changes li {
zoom: 1; zoom: 1;
display: inline; display: inline;
} }
\ No newline at end of file
#keyword_container .yui-ac-content {
_height: 30em; /* ie6 */
}
...@@ -527,7 +527,21 @@ input.required, select.required, span.required_explanation { ...@@ -527,7 +527,21 @@ input.required, select.required, span.required_explanation {
background-image: url(global/down.png); background-image: url(global/down.png);
} }
/* custom styles for inline instances of autocomplete input fields*/ /* custom styles for inline instances of autocomplete input fields */
.yui-skin-sam .yui-ac-input { position:static !important; .yui-skin-sam .yui-ac-input { position:static !important;
vertical-align:middle !important;} vertical-align:middle !important;
.yui-skin-sam .yui-ac-container { left:0px !important;} width:auto !important; }
.yui-skin-sam .yui-ac-container { left:0px !important; }
.yui-skin-sam .yui-ac { display: inline-block; }
#keyword_container {
padding-bottom: 2em;
padding-top: .2em;
}
#keyword_container .yui-ac-content {
max-height: 19em;
overflow: auto;
overflow-x: hidden;
margin-left: -1px;
}
...@@ -630,14 +630,11 @@ TUI_hide_default('expert_fields'); ...@@ -630,14 +630,11 @@ TUI_hide_default('expert_fields');
[% IF user.in_group('editbugs', product.id) %] [% IF user.in_group('editbugs', product.id) %]
[% IF use_keywords %] [% IF use_keywords %]
<tr> <tr>
[% INCLUDE "bug/field-label.html.tmpl" [% INCLUDE bug/field.html.tmpl
field = bug_fields.keywords editable = 1 bug = default, field = bug_fields.keywords, editable = 1,
desc_url = "describekeywords.cgi" value = keywords, desc_url = "describekeywords.cgi",
value_span = 2
%] %]
<td colspan="3">
<input id="keywords" name="keywords" size="40"
value="[% keywords FILTER html %]"> (optional)
</td>
</tr> </tr>
[% END %] [% END %]
......
...@@ -591,8 +591,13 @@ ...@@ -591,8 +591,13 @@
<label for="keywords" accesskey="k"> <label for="keywords" accesskey="k">
<b><a href="describekeywords.cgi"><u>K</u>eywords</a></b></label>: <b><a href="describekeywords.cgi"><u>K</u>eywords</a></b></label>:
</td> </td>
[% PROCESS input inputname => "keywords" size => 40 colspan => 2 <td class="field_value" colspan="2">
value => bug.keywords.join(', ') %] [% INCLUDE bug/field.html.tmpl
bug = bug, field = bug_fields.keywords, value = bug.keywords
editable = bug.check_can_change_field("keywords", 0, 1),
no_tds = 1
%]
</td>
</tr> </tr>
[% END %] [% END %]
[% END %] [% END %]
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
# Contributor(s): Myk Melez <myk@mozilla.org> # Contributor(s): Myk Melez <myk@mozilla.org>
# Max Kanat-Alexander <mkanat@bugzilla.org> # Max Kanat-Alexander <mkanat@bugzilla.org>
# Elliotte Martin <elliotte_martin@yahoo.com> # Elliotte Martin <elliotte_martin@yahoo.com>
# Guy Pyrzak <guy.pyrzak@gmail.com>
# Reed Loden <reed@reedloden.com>
#%] #%]
[%# INTERFACE: [%# INTERFACE:
...@@ -165,22 +167,35 @@ ...@@ -165,22 +167,35 @@
[% FOREACH url = value %] [% FOREACH url = value %]
<li> <li>
<a href="[% url FILTER html %]">[% url FILTER html %]</a> <a href="[% url FILTER html %]">[% url FILTER html %]</a>
[% IF editable %] <label><input type="checkbox" value="[% url FILTER html %]"
<label><input type="checkbox" value="[% url FILTER html %]" name="remove_[% field.name FILTER html %]">
name="remove_[% field.name FILTER html %]"> Remove</label>
Remove</label>
[% END %]
</li> </li>
[% END %] [% END %]
[% '</ul>' IF value.size %] [% '</ul>' IF value.size %]
[% IF editable && Param('use_see_also') %] [% IF Param('use_see_also') %]
<label for="[% field.name FILTER html %]"> <label for="[% field.name FILTER html %]">
<strong>Add [% terms.Bug %] URLs:</strong> <strong>Add [% terms.Bug %] URLs:</strong>
</label><br> </label><br>
<input type="text" id="[% field.name FILTER html %]" size="40" <input type="text" id="[% field.name FILTER html %]" size="40"
class="text_input" name="[% field.name FILTER html %]"> class="text_input" name="[% field.name FILTER html %]">
[% END %] [% END %]
[% CASE constants.FIELD_TYPE_KEYWORDS %]
<div id="keyword_container">
<input type="text" id="[% field.name FILTER html %]" size="40"
class="text_input" name="[% field.name FILTER html %]"
value="[% value FILTER html %]">
<div id="keyword_autocomplete"></div>
</div>
<script type="text/javascript" defer="defer">
YAHOO.bugzilla.keyword_array = [
[%- FOREACH keyword = all_keywords %]
[%-# %]"[% keyword.name FILTER js %]"
[%- "," IF NOT loop.last %][% END %]];
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete');
</script>
[% END %] [% END %]
[% ELSIF field.type == constants.FIELD_TYPE_TEXTAREA %] [% ELSIF field.type == constants.FIELD_TYPE_TEXTAREA %]
<div class="uneditable_textarea">[% value FILTER wrap_comment(60) <div class="uneditable_textarea">[% value FILTER wrap_comment(60)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
# Max Kanat-Alexander <mkanat@bugzilla.org> # Max Kanat-Alexander <mkanat@bugzilla.org>
# Frédéric Buclin <LpSolit@gmail.com> # Frédéric Buclin <LpSolit@gmail.com>
# Guy Pyrzak <guy.pyrzak@gmail.com> # Guy Pyrzak <guy.pyrzak@gmail.com>
# Reed Loden <reed@reedloden.com>
#%] #%]
[% PROCESS global/variables.none.tmpl %] [% PROCESS global/variables.none.tmpl %]
...@@ -173,7 +174,7 @@ ...@@ -173,7 +174,7 @@
id => "assigned_to" id => "assigned_to"
name => "assigned_to" name => "assigned_to"
value => dontchange value => dontchange
size => 32 size => 40
%] %]
<input type="checkbox" id="set_default_assignee" name="set_default_assignee" value="1"> <input type="checkbox" id="set_default_assignee" name="set_default_assignee" value="1">
<label for="set_default_assignee">Reset Assignee to default</label> <label for="set_default_assignee">Reset Assignee to default</label>
...@@ -188,7 +189,7 @@ ...@@ -188,7 +189,7 @@
id => "qa_contact" id => "qa_contact"
name => "qa_contact" name => "qa_contact"
value => dontchange value => dontchange
size => 32 size => 40
%] %]
<input type="checkbox" id="set_default_qa_contact" name="set_default_qa_contact" value="1"> <input type="checkbox" id="set_default_qa_contact" name="set_default_qa_contact" value="1">
<label for="set_default_qa_contact">Reset QA Contact to default</label> <label for="set_default_qa_contact">Reset QA Contact to default</label>
...@@ -200,7 +201,7 @@ ...@@ -200,7 +201,7 @@
<th><label for="masscc">CC List:</label></th> <th><label for="masscc">CC List:</label></th>
<td colspan="3"> <td colspan="3">
<input id="masscc" name="masscc" size="32"> <input id="masscc" name="masscc" size="40">
<select name="ccaction"> <select name="ccaction">
<option value="add">Add these to the CC List</option> <option value="add">Add these to the CC List</option>
<option value="remove">Remove these from the CC List</option> <option value="remove">Remove these from the CC List</option>
...@@ -212,13 +213,15 @@ ...@@ -212,13 +213,15 @@
[% IF use_keywords %] [% IF use_keywords %]
<tr> <tr>
<th> [% INCLUDE "bug/field-label.html.tmpl"
<label for="keywords"> field = bug_fields.keywords, editable = 1
<a href="describekeywords.cgi">Keywords</a>: desc_url = "describekeywords.cgi"
</label> %]
</th>
<td colspan="3"> <td colspan="3">
<input id="keywords" name="keywords" size="32"> [% INCLUDE bug/field.html.tmpl
field = bug_fields.keywords, editable = 1, value = keywords
no_tds = 1
%]
<select name="keywordaction"> <select name="keywordaction">
<option value="add">Add these keywords</option> <option value="add">Add these keywords</option>
<option value="delete">Delete these keywords</option> <option value="delete">Delete these keywords</option>
...@@ -236,7 +239,7 @@ ...@@ -236,7 +239,7 @@
</label> </label>
</th> </th>
<td colspan="3"> <td colspan="3">
<input id="dependson" name="dependson" size="32"> <input id="dependson" name="dependson" size="40">
<select name="dependson_action"> <select name="dependson_action">
<option value="add">Add these IDs</option> <option value="add">Add these IDs</option>
<option value="remove">Delete these IDs</option> <option value="remove">Delete these IDs</option>
...@@ -251,7 +254,7 @@ ...@@ -251,7 +254,7 @@
</label> </label>
</th> </th>
<td colspan="3"> <td colspan="3">
<input id="blocked" name="blocked" size="32"> <input id="blocked" name="blocked" size="40">
<select name="blocked_action"> <select name="blocked_action">
<option value="add">Add these IDs</option> <option value="add">Add these IDs</option>
<option value="remove">Delete these IDs</option> <option value="remove">Delete these IDs</option>
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# Initial Developer. All Rights Reserved. # Initial Developer. All Rights Reserved.
# #
# Contributor(s): Guy Pyrzak <guy.pyrzak@gmail.com> # Contributor(s): Guy Pyrzak <guy.pyrzak@gmail.com>
# Reed Loden <reed@reedloden.com>
# #
#%] #%]
[%# INTERFACE: [%# INTERFACE:
...@@ -35,16 +36,41 @@ ...@@ -35,16 +36,41 @@
tag_name = "span" tag_name = "span"
editable = 1 editable = 1
%] %]
[% INCLUDE "search/type-select.html.tmpl" [% INCLUDE "search/type-select.html.tmpl"
name = field.name _ "_type", name = field.name _ "_type",
types = types, types = types,
selected = type_selected %] selected = type_selected
%]
<input name="[% field.name FILTER html %]" <input name="[% field.name FILTER html %]"
id="[% field.name FILTER html %]" size="40" id="[% field.name FILTER html %]" size="40"
[% IF onchange %] onchange="[% onchange FILTER html %]"[% END %] [% IF onchange %] onchange="[% onchange FILTER html %]"[% END %]
value="[% value FILTER html %]"> value="[% value FILTER html %]">
[% CASE constants.FIELD_TYPE_KEYWORDS %]
[% INCLUDE "bug/field-label.html.tmpl"
field = field
tag_name = "span"
editable = 1
%]
[% INCLUDE "search/type-select.html.tmpl"
name = field.name _ "_type",
types = types,
selected = type_selected
%]
<div id="keyword_container">
<input name="[% field.name FILTER html %]"
id="[% field.name FILTER html %]" size="40"
[% IF onchange %] onchange="[% onchange FILTER html %]"[% END %]
value="[% value FILTER html %]">
<div id="keyword_autocomplete"></div>
</div>
<script type="text/javascript" defer="defer">
YAHOO.bugzilla.keyword_array = [
[%- FOREACH keyword = all_keywords %]
[%-# %]"[% keyword.name FILTER js %]"
[%- "," IF NOT loop.last %][% END %]];
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete');
</script>
[% CASE constants.FIELD_TYPE_DATETIME %] [% CASE constants.FIELD_TYPE_DATETIME %]
[% INCLUDE "bug/field-label.html.tmpl" [% INCLUDE "bug/field-label.html.tmpl"
field = field field = field
...@@ -113,5 +139,4 @@ ...@@ -113,5 +139,4 @@
[% END %] [% END %]
</select> </select>
</div> </div>
[% END %] [% END %]
\ No newline at end of file
...@@ -38,7 +38,7 @@ var queryform = "queryform" ...@@ -38,7 +38,7 @@ var queryform = "queryform"
title = "Search for $terms.bugs" title = "Search for $terms.bugs"
onload = "doOnSelectProduct(0); enableHelp();" onload = "doOnSelectProduct(0); enableHelp();"
javascript = js_data javascript = js_data
yui = [ 'calendar' ] yui = [ 'autocomplete', 'calendar' ]
javascript_urls = [ "js/productform.js", "js/util.js", javascript_urls = [ "js/productform.js", "js/util.js",
"js/help.js" , "js/TUI.js", "js/field.js"] "js/help.js" , "js/TUI.js", "js/field.js"]
style_urls = [ "skins/standard/help.css" , "skins/standard/search_form.css" ] style_urls = [ "skins/standard/help.css" , "skins/standard/search_form.css" ]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment