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 => (
{name => "owner_idle_time", desc => "Time Since Assignee Touched"},
{name => 'see_also', desc => "See Also",
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 {
csv_colsepchar => { options => [',',';'], default => ',' },
# 2005-10-26 wurblzap@gmail.com -- Bug 291459
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
state_addselfcc => { options => ['always', 'never', 'cc_unless_role'],
default => 'cc_unless_role' },
......@@ -192,6 +190,9 @@ sub update_settings {
$settings{$setting}->{subclass}, undef,
!$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 {
......
......@@ -928,7 +928,9 @@ sub create {
'use_keywords' => sub { return Bugzilla::Keyword->any_exist; },
# 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(@_); },
......
......@@ -23,10 +23,8 @@ use Bugzilla::Search;
use Bugzilla::Search::Quicksearch;
use Bugzilla::Search::Recent;
use Bugzilla::Search::Saved;
use Bugzilla::User;
use Bugzilla::Bug;
use Bugzilla::Product;
use Bugzilla::Keyword;
use Bugzilla::Field;
use Bugzilla::Status;
use Bugzilla::Token;
......@@ -445,51 +443,18 @@ elsif (($cmdtype eq "doit") && defined $cgi->param('remtype')) {
my $new_query = $cgi->param('newquery');
my $token = $cgi->param('token');
check_hash_token($token, ['savedsearch']);
# If list_of_bugs is true, we are adding/removing tags to/from
# individual bugs.
if ($cgi->param('list_of_bugs')) {
# 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];
my $existed_before = InsertNamedQuery($query_name, $new_query, 1);
if ($existed_before) {
$vars->{'message'} = "buglist_updated_named_query";
}
else {
my $existed_before = InsertNamedQuery($query_name, $new_query, 1);
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->{'message'} = "buglist_new_named_query";
}
$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();
$template->process("global/message.html.tmpl", $vars)
......
......@@ -870,27 +870,29 @@ YAHOO.bugzilla.userAutocomplete = {
}
};
YAHOO.bugzilla.keywordAutocomplete = {
dataSource : null,
init_ds : function(){
this.dataSource = new YAHOO.util.LocalDataSource( YAHOO.bugzilla.keyword_array );
YAHOO.bugzilla.fieldAutocomplete = {
dataSource : [],
init_ds : function( field ) {
this.dataSource[field] =
new YAHOO.util.LocalDataSource( YAHOO.bugzilla.field_array[field] );
},
init : function( field, container ) {
if( this.dataSource == null ){
this.init_ds();
if( this.dataSource[field] == null ) {
this.init_ds( field );
}
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
var fieldAutoComp =
new YAHOO.widget.AutoComplete(field, container, this.dataSource[field]);
fieldAutoComp.maxResultsDisplayed = YAHOO.bugzilla.field_array[field].length;
fieldAutoComp.minQueryLength = 0;
fieldAutoComp.useIFrame = true;
fieldAutoComp.delimChar = [","," "];
fieldAutoComp.resultTypeList = false;
fieldAutoComp.queryDelay = 0;
/* Causes all the possibilities in the field to appear when a user
* focuses on the textbox
*/
keywordAutoComp.textboxFocusEvent.subscribe( function(){
var sInputValue = YAHOO.util.Dom.get('keywords').value;
fieldAutoComp.textboxFocusEvent.subscribe( function(){
var sInputValue = YAHOO.util.Dom.get(field).value;
if( sInputValue.length === 0 ){
this.sendQuery(sInputValue);
this.collapseContainer();
......
......@@ -28,10 +28,6 @@ use Bugzilla::Bug;
use Bugzilla::User;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Field;
use Bugzilla::Product;
use Bugzilla::Component;
use Bugzilla::Keyword;
use Bugzilla::Flag;
use Bugzilla::Status;
use Bugzilla::Token;
......@@ -330,6 +326,14 @@ if (defined $cgi->param('id')) {
my ($flags, $new_flags) = Bugzilla::Flag->extract_flags_from_cgi(
$first_bug, undef, $vars);
$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 {
display: inline;
}
#keyword_container .yui-ac-content {
#keywords_container .yui-ac-content {
_height: 30em; /* ie6 */
}
......
......@@ -526,12 +526,12 @@ input.required, select.required, span.required_explanation {
overflow-x: hidden;
}
#keyword_container {
#keywords_container {
padding-top: .2em;
}
#keyword_container .yui-ac-content {
#keywords_container .yui-ac-content {
margin-left: -1px;
}
......
......@@ -99,7 +99,7 @@
width: inherit;
}
#keyword_container {
#keywords_container {
padding-bottom: 0;
}
......
......@@ -118,7 +118,7 @@ table#flags {
float: right;
}
.text_input, .bz_userfield, #keyword_container {
.text_input, .bz_userfield, #keywords_container, #tag_container {
width: 100%;
}
.bz_bug .bz_alias_short_desc_container {
......
......@@ -523,11 +523,20 @@
[% 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
no_tds = 1, possible_values = all_keywords
%]
</td>
</tr>
[% 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 %]
[%############################################################################%]
......
......@@ -137,6 +137,13 @@ status_whiteboard =>
"Each $terms.bug has a free-form single line text entry box for"
_ " 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 =>
"The $vars.field_descs.target_milestone field is used to define when the"
_ " engineer the $terms.bug is assigned to expects to fix it.",
......
......@@ -171,19 +171,21 @@
</script>
[% END %]
[% 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"
class="text_input" name="[% field.name FILTER html %]"
value="[% value FILTER html %]">
<div id="keyword_autocomplete"></div>
<div id="[% field.name FILTER html %]_autocomplete"></div>
</div>
<script type="text/javascript" defer="defer">
YAHOO.bugzilla.keyword_array = [
[%- FOREACH keyword = all_keywords %]
[%-# %]"[% keyword.name FILTER js %]"
if (typeof YAHOO.bugzilla.field_array === "undefined")
YAHOO.bugzilla.field_array = [];
YAHOO.bugzilla.field_array["[% field.name FILTER js %]"] = [
[%- FOREACH val = possible_values %]
[%-# %]"[% val FILTER js %]"
[%- "," IF NOT loop.last %][% END %]];
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete');
YAHOO.bugzilla.fieldAutocomplete.init('[% field.name FILTER js %]',
'[% field.name FILTER js %]_autocomplete');
</script>
[% END %]
[% ELSE %]
......
......@@ -177,10 +177,6 @@
'series.frequency * 2',
],
'global/per-bug-queries.html.tmpl' => [
'" value=\"$bugids\"" IF bugids',
],
'global/select-menu.html.tmpl' => [
'options',
'size',
......
......@@ -118,7 +118,7 @@
"settings" => "Settings",
"short_desc" => "Summary",
"status_whiteboard" => "Whiteboard",
"tag.name" => "Tags",
"tag" => "Tags",
"target_milestone" => "Target Milestone",
"version" => "Version",
"work_time" => "Hours Worked",
......
......@@ -849,18 +849,6 @@
The cookie that was remembering your login is now gone.
[% 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" %]
[% 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 @@
"off" => "Off",
"oldest_to_newest" => "Oldest to Newest",
"on" => "On",
"per_bug_queries" => "Enable tags for $terms.bugs",
"post_bug_submit_action" => "After changing $terms.abug",
"next_bug" => "Show next $terms.bug in my list",
"same_bug" => "Show the updated $terms.bug",
......
......@@ -58,8 +58,6 @@
[%# Individual bugs addition %]
[% PROCESS "global/per-bug-queries.html.tmpl" %]
[%# Sections of links to more things users can do on this installation. %]
[% Hook.process("end") %]
</ul>
......@@ -35,20 +35,22 @@
types = types,
selected = type_selected
%]
<div id="keyword_container">
<div id="[% field.name FILTER html %]_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 id="[% field.name FILTER html %]_autocomplete"></div>
</div>
<script type="text/javascript" defer="defer">
YAHOO.bugzilla.keyword_array = [
[%- FOREACH keyword = all_keywords %]
[%-# %]"[% keyword.name FILTER js %]"
if (typeof YAHOO.bugzilla.field_array === "undefined")
YAHOO.bugzilla.field_array = [];
YAHOO.bugzilla.field_array["[% field.name FILTER js %]"] = [
[%- FOREACH val = possible_values %]
[%-# %]"[% val FILTER js %]"
[%- "," IF NOT loop.last %][% END %]];
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete');
YAHOO.bugzilla.fieldAutocomplete.init('[% field.name FILTER js %]',
'[% field.name FILTER js %]_autocomplete');
</script>
[% CASE constants.FIELD_TYPE_DATETIME %]
[% INCLUDE "bug/field-label.html.tmpl"
......
......@@ -142,6 +142,7 @@ TUI_hide_default('information_query');
%]
<div class="search_field_row">
[% type = field_container.field.name _ "_type" %]
[% possible_values = field_container.field.name == 'keywords' ? all_keywords : [] %]
[% INCLUDE "search/field.html.tmpl"
field => field_container.field
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