Commit 92a81752 authored by Frédéric Buclin's avatar Frédéric Buclin

Bug 616191: Implement UI to easily tag bugs from the bug report directly (and…

Bug 616191: Implement UI to easily tag bugs from the bug report directly (and get rid of the current form in the footer) r=timello a=LpSolit
parent 4e5dcf36
...@@ -248,7 +248,8 @@ use constant DEFAULT_FIELDS => ( ...@@ -248,7 +248,8 @@ use constant DEFAULT_FIELDS => (
{name => "owner_idle_time", desc => "Time Since Assignee Touched"}, {name => "owner_idle_time", desc => "Time Since Assignee Touched"},
{name => 'see_also', desc => "See Also", {name => 'see_also', desc => "See Also",
type => FIELD_TYPE_BUG_URLS}, type => FIELD_TYPE_BUG_URLS},
{name => 'tag', desc => 'Tags', buglist => 1}, {name => 'tag', desc => 'Tags', buglist => 1,
type => FIELD_TYPE_KEYWORDS},
); );
################ ################
......
...@@ -61,8 +61,6 @@ sub SETTINGS { ...@@ -61,8 +61,6 @@ sub SETTINGS {
csv_colsepchar => { options => [',',';'], default => ',' }, csv_colsepchar => { options => [',',';'], default => ',' },
# 2005-10-26 wurblzap@gmail.com -- Bug 291459 # 2005-10-26 wurblzap@gmail.com -- Bug 291459
zoom_textareas => { options => ["on", "off"], default => "on" }, zoom_textareas => { options => ["on", "off"], default => "on" },
# 2005-10-21 LpSolit@gmail.com -- Bug 313020
per_bug_queries => { options => ['on', 'off'], default => 'off' },
# 2006-05-01 olav@bkor.dhs.org -- Bug 7710 # 2006-05-01 olav@bkor.dhs.org -- Bug 7710
state_addselfcc => { options => ['always', 'never', 'cc_unless_role'], state_addselfcc => { options => ['always', 'never', 'cc_unless_role'],
default => 'cc_unless_role' }, default => 'cc_unless_role' },
...@@ -192,6 +190,9 @@ sub update_settings { ...@@ -192,6 +190,9 @@ sub update_settings {
$settings{$setting}->{subclass}, undef, $settings{$setting}->{subclass}, undef,
!$any_settings); !$any_settings);
} }
# Delete the obsolete 'per_bug_queries' user preference. Bug 616191.
$dbh->do('DELETE FROM setting WHERE name = ?', undef, 'per_bug_queries');
} }
sub update_system_groups { sub update_system_groups {
......
...@@ -928,7 +928,9 @@ sub create { ...@@ -928,7 +928,9 @@ sub create {
'use_keywords' => sub { return Bugzilla::Keyword->any_exist; }, 'use_keywords' => sub { return Bugzilla::Keyword->any_exist; },
# All the keywords. # All the keywords.
'all_keywords' => sub { return Bugzilla::Keyword->get_all(); }, 'all_keywords' => sub {
return [map { $_->name } Bugzilla::Keyword->get_all()];
},
'feature_enabled' => sub { return Bugzilla->feature(@_); }, 'feature_enabled' => sub { return Bugzilla->feature(@_); },
......
...@@ -23,10 +23,8 @@ use Bugzilla::Search; ...@@ -23,10 +23,8 @@ use Bugzilla::Search;
use Bugzilla::Search::Quicksearch; use Bugzilla::Search::Quicksearch;
use Bugzilla::Search::Recent; use Bugzilla::Search::Recent;
use Bugzilla::Search::Saved; use Bugzilla::Search::Saved;
use Bugzilla::User;
use Bugzilla::Bug; use Bugzilla::Bug;
use Bugzilla::Product; use Bugzilla::Product;
use Bugzilla::Keyword;
use Bugzilla::Field; use Bugzilla::Field;
use Bugzilla::Status; use Bugzilla::Status;
use Bugzilla::Token; use Bugzilla::Token;
...@@ -445,51 +443,18 @@ elsif (($cmdtype eq "doit") && defined $cgi->param('remtype')) { ...@@ -445,51 +443,18 @@ elsif (($cmdtype eq "doit") && defined $cgi->param('remtype')) {
my $new_query = $cgi->param('newquery'); my $new_query = $cgi->param('newquery');
my $token = $cgi->param('token'); my $token = $cgi->param('token');
check_hash_token($token, ['savedsearch']); check_hash_token($token, ['savedsearch']);
# If list_of_bugs is true, we are adding/removing tags to/from my $existed_before = InsertNamedQuery($query_name, $new_query, 1);
# individual bugs. if ($existed_before) {
if ($cgi->param('list_of_bugs')) { $vars->{'message'} = "buglist_updated_named_query";
# We add/remove tags based on the action choosen.
my $action = trim($cgi->param('action') || '');
$action =~ /^(add|remove)$/
|| ThrowUserError('unknown_action', {action => $action});
my $method = "${action}_tag";
# If no new tag name has been given, use the selected one.
$query_name ||= $cgi->param('oldqueryname')
or ThrowUserError('no_tag_to_edit', {action => $action});
my @buglist;
# Validate all bug IDs before editing tags in any of them.
foreach my $bug_id (split(/[\s,]+/, $cgi->param('bug_ids'))) {
next unless $bug_id;
push(@buglist, Bugzilla::Bug->check($bug_id));
}
foreach my $bug (@buglist) {
$bug->$method($query_name);
}
$vars->{'message'} = 'tag_updated';
$vars->{'action'} = $action;
$vars->{'tag'} = $query_name;
$vars->{'buglist'} = [map { $_->id } @buglist];
} }
else { else {
my $existed_before = InsertNamedQuery($query_name, $new_query, 1); $vars->{'message'} = "buglist_new_named_query";
if ($existed_before) {
$vars->{'message'} = "buglist_updated_named_query";
}
else {
$vars->{'message'} = "buglist_new_named_query";
}
# Make sure to invalidate any cached query data, so that the footer is
# correctly displayed
$user->flush_queries_cache();
$vars->{'queryname'} = $query_name;
} }
$vars->{'queryname'} = $query_name;
# Make sure to invalidate any cached query data, so that the footer is
# correctly displayed
$user->flush_queries_cache();
print $cgi->header(); print $cgi->header();
$template->process("global/message.html.tmpl", $vars) $template->process("global/message.html.tmpl", $vars)
......
...@@ -870,27 +870,29 @@ YAHOO.bugzilla.userAutocomplete = { ...@@ -870,27 +870,29 @@ YAHOO.bugzilla.userAutocomplete = {
} }
}; };
YAHOO.bugzilla.keywordAutocomplete = { YAHOO.bugzilla.fieldAutocomplete = {
dataSource : null, dataSource : [],
init_ds : function(){ init_ds : function( field ) {
this.dataSource = new YAHOO.util.LocalDataSource( YAHOO.bugzilla.keyword_array ); this.dataSource[field] =
new YAHOO.util.LocalDataSource( YAHOO.bugzilla.field_array[field] );
}, },
init : function( field, container ) { init : function( field, container ) {
if( this.dataSource == null ){ if( this.dataSource[field] == null ) {
this.init_ds(); this.init_ds( field );
} }
var keywordAutoComp = new YAHOO.widget.AutoComplete(field, container, this.dataSource); var fieldAutoComp =
keywordAutoComp.maxResultsDisplayed = YAHOO.bugzilla.keyword_array.length; new YAHOO.widget.AutoComplete(field, container, this.dataSource[field]);
keywordAutoComp.minQueryLength = 0; fieldAutoComp.maxResultsDisplayed = YAHOO.bugzilla.field_array[field].length;
keywordAutoComp.useIFrame = true; fieldAutoComp.minQueryLength = 0;
keywordAutoComp.delimChar = [","," "]; fieldAutoComp.useIFrame = true;
keywordAutoComp.resultTypeList = false; fieldAutoComp.delimChar = [","," "];
keywordAutoComp.queryDelay = 0; fieldAutoComp.resultTypeList = false;
/* Causes all the possibilities in the keyword to appear when a user fieldAutoComp.queryDelay = 0;
/* Causes all the possibilities in the field to appear when a user
* focuses on the textbox * focuses on the textbox
*/ */
keywordAutoComp.textboxFocusEvent.subscribe( function(){ fieldAutoComp.textboxFocusEvent.subscribe( function(){
var sInputValue = YAHOO.util.Dom.get('keywords').value; var sInputValue = YAHOO.util.Dom.get(field).value;
if( sInputValue.length === 0 ){ if( sInputValue.length === 0 ){
this.sendQuery(sInputValue); this.sendQuery(sInputValue);
this.collapseContainer(); this.collapseContainer();
......
...@@ -28,10 +28,6 @@ use Bugzilla::Bug; ...@@ -28,10 +28,6 @@ use Bugzilla::Bug;
use Bugzilla::User; use Bugzilla::User;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Field;
use Bugzilla::Product;
use Bugzilla::Component;
use Bugzilla::Keyword;
use Bugzilla::Flag; use Bugzilla::Flag;
use Bugzilla::Status; use Bugzilla::Status;
use Bugzilla::Token; use Bugzilla::Token;
...@@ -330,6 +326,14 @@ if (defined $cgi->param('id')) { ...@@ -330,6 +326,14 @@ if (defined $cgi->param('id')) {
my ($flags, $new_flags) = Bugzilla::Flag->extract_flags_from_cgi( my ($flags, $new_flags) = Bugzilla::Flag->extract_flags_from_cgi(
$first_bug, undef, $vars); $first_bug, undef, $vars);
$first_bug->set_flags($flags, $new_flags); $first_bug->set_flags($flags, $new_flags);
# Tags can only be set to one bug at once.
if (should_set('tag')) {
my @new_tags = split(/[\s,]+/, $cgi->param('tag'));
my ($tags_removed, $tags_added) = diff_arrays($first_bug->tags, \@new_tags);
$first_bug->remove_tag($_) foreach @$tags_removed;
$first_bug->add_tag($_) foreach @$tags_added;
}
} }
############################## ##############################
......
...@@ -44,7 +44,7 @@ form#Create #comp_desc { ...@@ -44,7 +44,7 @@ form#Create #comp_desc {
display: inline; display: inline;
} }
#keyword_container .yui-ac-content { #keywords_container .yui-ac-content {
_height: 30em; /* ie6 */ _height: 30em; /* ie6 */
} }
......
...@@ -526,12 +526,12 @@ input.required, select.required, span.required_explanation { ...@@ -526,12 +526,12 @@ input.required, select.required, span.required_explanation {
overflow-x: hidden; overflow-x: hidden;
} }
#keyword_container { #keywords_container {
padding-top: .2em; padding-top: .2em;
} }
#keyword_container .yui-ac-content { #keywords_container .yui-ac-content {
margin-left: -1px; margin-left: -1px;
} }
......
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
width: inherit; width: inherit;
} }
#keyword_container { #keywords_container {
padding-bottom: 0; padding-bottom: 0;
} }
......
...@@ -118,7 +118,7 @@ table#flags { ...@@ -118,7 +118,7 @@ table#flags {
float: right; float: right;
} }
.text_input, .bz_userfield, #keyword_container { .text_input, .bz_userfield, #keywords_container, #tag_container {
width: 100%; width: 100%;
} }
.bz_bug .bz_alias_short_desc_container { .bz_bug .bz_alias_short_desc_container {
......
...@@ -523,11 +523,20 @@ ...@@ -523,11 +523,20 @@
[% INCLUDE bug/field.html.tmpl [% INCLUDE bug/field.html.tmpl
bug = bug, field = bug_fields.keywords, value = bug.keywords bug = bug, field = bug_fields.keywords, value = bug.keywords
editable = bug.check_can_change_field("keywords", 0, 1), editable = bug.check_can_change_field("keywords", 0, 1),
no_tds = 1 no_tds = 1, possible_values = all_keywords
%] %]
</td> </td>
</tr> </tr>
[% END %] [% END %]
[% IF user.id %]
<tr>
[% INCLUDE bug/field.html.tmpl
bug = bug, field = bug_fields.tag, value = bug.tags.join(", "),
editable = 1, possible_values = user.tags.keys
%]
</tr>
[% END %]
[% END %] [% END %]
[%############################################################################%] [%############################################################################%]
......
...@@ -137,6 +137,13 @@ status_whiteboard => ...@@ -137,6 +137,13 @@ status_whiteboard =>
"Each $terms.bug has a free-form single line text entry box for" "Each $terms.bug has a free-form single line text entry box for"
_ " adding tags and status information.", _ " adding tags and status information.",
tag =>
"Unlike ${vars.field_descs.keywords} which are global and visible by
all users, ${vars.field_descs.tag} are personal and can only be
viewed and edited by their author.
Editing them won't send any notification to other users. Use them
to tag and keep track of ${terms.bugs}.",
target_milestone => target_milestone =>
"The $vars.field_descs.target_milestone field is used to define when the" "The $vars.field_descs.target_milestone field is used to define when the"
_ " engineer the $terms.bug is assigned to expects to fix it.", _ " engineer the $terms.bug is assigned to expects to fix it.",
......
...@@ -171,19 +171,21 @@ ...@@ -171,19 +171,21 @@
</script> </script>
[% END %] [% END %]
[% CASE constants.FIELD_TYPE_KEYWORDS %] [% CASE constants.FIELD_TYPE_KEYWORDS %]
<div id="keyword_container"> <div id="[% field.name FILTER html %]_container">
<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 %]"
value="[% value FILTER html %]"> value="[% value FILTER html %]">
<div id="keyword_autocomplete"></div> <div id="[% field.name FILTER html %]_autocomplete"></div>
</div> </div>
<script type="text/javascript" defer="defer"> <script type="text/javascript" defer="defer">
YAHOO.bugzilla.keyword_array = [ if (typeof YAHOO.bugzilla.field_array === "undefined")
[%- FOREACH keyword = all_keywords %] YAHOO.bugzilla.field_array = [];
[%-# %]"[% keyword.name FILTER js %]" YAHOO.bugzilla.field_array["[% field.name FILTER js %]"] = [
[%- FOREACH val = possible_values %]
[%-# %]"[% val FILTER js %]"
[%- "," IF NOT loop.last %][% END %]]; [%- "," IF NOT loop.last %][% END %]];
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]', YAHOO.bugzilla.fieldAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete'); '[% field.name FILTER js %]_autocomplete');
</script> </script>
[% END %] [% END %]
[% ELSE %] [% ELSE %]
......
...@@ -177,10 +177,6 @@ ...@@ -177,10 +177,6 @@
'series.frequency * 2', 'series.frequency * 2',
], ],
'global/per-bug-queries.html.tmpl' => [
'" value=\"$bugids\"" IF bugids',
],
'global/select-menu.html.tmpl' => [ 'global/select-menu.html.tmpl' => [
'options', 'options',
'size', 'size',
......
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
"settings" => "Settings", "settings" => "Settings",
"short_desc" => "Summary", "short_desc" => "Summary",
"status_whiteboard" => "Whiteboard", "status_whiteboard" => "Whiteboard",
"tag.name" => "Tags", "tag" => "Tags",
"target_milestone" => "Target Milestone", "target_milestone" => "Target Milestone",
"version" => "Version", "version" => "Version",
"work_time" => "Hours Worked", "work_time" => "Hours Worked",
......
...@@ -849,18 +849,6 @@ ...@@ -849,18 +849,6 @@
The cookie that was remembering your login is now gone. The cookie that was remembering your login is now gone.
[% END %] [% END %]
[% ELSIF message_tag == "tag_updated" %]
[% title = "Tag Updated" %]
The '<a href="buglist.cgi?tag=[% tag FILTER uri %]">[% tag FILTER html %]</a>'
tag has been
[% IF action == "add" %]
added to
[% ELSE %]
removed from
[% END %]
[%+ buglist.size > 1 ? terms.bugs : terms.bug %]
[%+ buglist.join(", ") FILTER html %].
[% ELSIF message_tag == "term" %] [% ELSIF message_tag == "term" %]
[% terms.$term FILTER html %] [% terms.$term FILTER html %]
......
[%# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
#%]
[% IF user.id && user.settings.per_bug_queries.value == "on" %]
<li id="links-special">
<script type="text/javascript">
<!--
function update_text() {
// 'lob' means list_of_bugs.
var lob_action = document.getElementById('lob_action');
var action = lob_action.options[lob_action.selectedIndex].value;
var text = document.getElementById('lob_direction');
var new_query_text = document.getElementById('lob_new_query_text');
if (action == "add") {
text.innerHTML = "to";
new_query_text.style.display = 'inline';
}
else {
text.innerHTML = "from";
new_query_text.style.display = 'none';
}
}
function manage_old_lists() {
var old_lists = document.getElementById('lob_oldqueryname');
// If there is no saved searches available, returns.
if (!old_lists) return;
var new_query = document.getElementById('lob_newqueryname').value;
if (new_query != "") {
old_lists.disabled = true;
}
else {
old_lists.disabled = false;
}
}
//-->
</script>
<div class="label"></div>
<ul class="links"><li class="form">
<form id="list_of_bugs" action="buglist.cgi" method="get">
<input type="hidden" name="cmdtype" value="doit">
<input type="hidden" name="remtype" value="asnamed">
<input type="hidden" name="list_of_bugs" value="1">
<input type="hidden" name="token" value="[% issue_hash_token(['savedsearch']) FILTER html %]">
<select id="lob_action" name="action" onchange="update_text();">
<option value="add">Add</option>
[% IF user.tags.size %]
<option value="remove">Remove</option>
[% END %]
</select>
[% IF Param('docs_urlbase') %]
<a href="[% docs_urlbase FILTER html %]query.html#individual-buglists">the named tag</a>
[% ELSE %]
the named tag
[% END %]
[% IF user.tags.size %]
<select id="lob_oldqueryname" name="oldqueryname">
[% FOREACH tag = user.tags.keys %]
<option value="[% tag FILTER html %]">[% tag FILTER html %]</option>
[% END %]
</select>
[% END %]
<span id="lob_new_query_text">
[% " or create and add the tag" IF user.tags.size %]
<input class="txt" type="text" id="lob_newqueryname"
size="20" maxlength="64" name="newqueryname"
onkeyup="manage_old_lists();">
</span>
<span id="lob_direction">to</span>
[%+ terms.bugs %]
<input type="text" name="bug_ids" size="12" maxlength="80"
[%- " value=\"$bugids\"" IF bugids %]>
<input type="submit" value="Commit" id="commit_list_of_bugs">
</form>
</li></ul>
</li>
[% END %]
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
"off" => "Off", "off" => "Off",
"oldest_to_newest" => "Oldest to Newest", "oldest_to_newest" => "Oldest to Newest",
"on" => "On", "on" => "On",
"per_bug_queries" => "Enable tags for $terms.bugs",
"post_bug_submit_action" => "After changing $terms.abug", "post_bug_submit_action" => "After changing $terms.abug",
"next_bug" => "Show next $terms.bug in my list", "next_bug" => "Show next $terms.bug in my list",
"same_bug" => "Show the updated $terms.bug", "same_bug" => "Show the updated $terms.bug",
......
...@@ -58,8 +58,6 @@ ...@@ -58,8 +58,6 @@
[%# Individual bugs addition %] [%# Individual bugs addition %]
[% PROCESS "global/per-bug-queries.html.tmpl" %]
[%# Sections of links to more things users can do on this installation. %] [%# Sections of links to more things users can do on this installation. %]
[% Hook.process("end") %] [% Hook.process("end") %]
</ul> </ul>
...@@ -35,20 +35,22 @@ ...@@ -35,20 +35,22 @@
types = types, types = types,
selected = type_selected selected = type_selected
%] %]
<div id="keyword_container"> <div id="[% field.name FILTER html %]_container">
<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 %]">
<div id="keyword_autocomplete"></div> <div id="[% field.name FILTER html %]_autocomplete"></div>
</div> </div>
<script type="text/javascript" defer="defer"> <script type="text/javascript" defer="defer">
YAHOO.bugzilla.keyword_array = [ if (typeof YAHOO.bugzilla.field_array === "undefined")
[%- FOREACH keyword = all_keywords %] YAHOO.bugzilla.field_array = [];
[%-# %]"[% keyword.name FILTER js %]" YAHOO.bugzilla.field_array["[% field.name FILTER js %]"] = [
[%- FOREACH val = possible_values %]
[%-# %]"[% val FILTER js %]"
[%- "," IF NOT loop.last %][% END %]]; [%- "," IF NOT loop.last %][% END %]];
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]', YAHOO.bugzilla.fieldAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete'); '[% field.name FILTER js %]_autocomplete');
</script> </script>
[% CASE constants.FIELD_TYPE_DATETIME %] [% CASE constants.FIELD_TYPE_DATETIME %]
[% INCLUDE "bug/field-label.html.tmpl" [% INCLUDE "bug/field-label.html.tmpl"
......
...@@ -142,6 +142,7 @@ TUI_hide_default('information_query'); ...@@ -142,6 +142,7 @@ TUI_hide_default('information_query');
%] %]
<div class="search_field_row"> <div class="search_field_row">
[% type = field_container.field.name _ "_type" %] [% type = field_container.field.name _ "_type" %]
[% possible_values = field_container.field.name == 'keywords' ? all_keywords : [] %]
[% INCLUDE "search/field.html.tmpl" [% INCLUDE "search/field.html.tmpl"
field => field_container.field field => field_container.field
types => field_container.qtypes || query_types types => field_container.qtypes || query_types
......
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