summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Lawrence <dlawrence@mozilla.com>2011-09-02 21:50:06 +0200
committerDavid Lawrence <dlawrence@mozilla.com>2011-09-02 21:50:06 +0200
commitd3144ccde7c1d3c432baaf43fe0b79a6c30eb7b0 (patch)
tree8f7fac4d4b98c80a090e74934a754443c127d846
parent9344a458c830ba066b9004c301d0fc6cabc8f229 (diff)
parentbf9156ad5f0a57a467dd46292efb2f0a47ec845e (diff)
downloadbugzilla-d3144ccde7c1d3c432baaf43fe0b79a6c30eb7b0.tar.gz
bugzilla-d3144ccde7c1d3c432baaf43fe0b79a6c30eb7b0.tar.xz
merged with bugzilla/4.2
-rw-r--r--Bugzilla/Bug.pm8
-rw-r--r--Bugzilla/DB.pm14
-rw-r--r--Bugzilla/DB/Schema.pm6
-rw-r--r--Bugzilla/DB/Schema/Sqlite.pm17
-rw-r--r--Bugzilla/DB/Sqlite.pm45
-rw-r--r--Bugzilla/Install/DB.pm20
-rw-r--r--Bugzilla/Migrate.pm1
-rw-r--r--Bugzilla/Search.pm4
-rw-r--r--Bugzilla/User.pm4
-rw-r--r--contrib/README18
-rwxr-xr-xcontrib/yp_nomail.sh77
-rwxr-xr-xeditusers.cgi4
-rwxr-xr-xquery.cgi3
-rw-r--r--t/004template.t44
-rw-r--r--t/Support/Templates.pm13
-rw-r--r--template/en/default/bug/field-help.none.tmpl2
-rw-r--r--template/en/default/global/field-descs.none.tmpl2
17 files changed, 137 insertions, 145 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index 6fdab3645..8beecdcd2 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -2918,10 +2918,10 @@ sub add_tag {
my $tag_id = $user->tags->{$tag}->{id};
# If this tag doesn't exist for this user yet, create it.
if (!$tag_id) {
- $dbh->do('INSERT INTO tags (user_id, name) VALUES (?, ?)',
+ $dbh->do('INSERT INTO tag (user_id, name) VALUES (?, ?)',
undef, ($user->id, $tag));
- $tag_id = $dbh->selectrow_array('SELECT id FROM tags
+ $tag_id = $dbh->selectrow_array('SELECT id FROM tag
WHERE name = ? AND user_id = ?',
undef, ($tag, $user->id));
# The list has changed.
@@ -2957,7 +2957,7 @@ sub remove_tag {
# Decrement the counter, and delete the tag if no bugs are using it anymore.
if (!--$user->tags->{$tag}->{bug_count}) {
- $dbh->do('DELETE FROM tags WHERE name = ? AND user_id = ?',
+ $dbh->do('DELETE FROM tag WHERE name = ? AND user_id = ?',
undef, ($tag, $user->id));
# The list has changed.
@@ -2974,7 +2974,7 @@ sub tags {
if (!exists $self->{tags}) {
$self->{tags} = $dbh->selectcol_arrayref(
'SELECT name FROM bug_tag
- INNER JOIN tags ON tags.id = bug_tag.tag_id
+ INNER JOIN tag ON tag.id = bug_tag.tag_id
WHERE bug_id = ? AND user_id = ?',
undef, ($self->id, $user->id));
}
diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm
index 8d1cf32a0..a537d6131 100644
--- a/Bugzilla/DB.pm
+++ b/Bugzilla/DB.pm
@@ -93,6 +93,12 @@ use constant FULLTEXT_OR => '';
use constant WORD_START => '(^|[^[:alnum:]])';
use constant WORD_END => '($|[^[:alnum:]])';
+# On most databases, in order to drop an index, you have to first drop
+# the foreign keys that use that index. However, on some databases,
+# dropping the FK immediately before dropping the index causes problems
+# and doesn't need to be done anyway, so those DBs set this to 0.
+use constant INDEX_DROPS_REQUIRE_FK_DROPS => 1;
+
#####################################################################
# Overridden Superclass Methods
#####################################################################
@@ -947,9 +953,11 @@ sub bz_drop_index {
my $index_exists = $self->bz_index_info($table, $name);
if ($index_exists) {
- # We cannot delete an index used by a FK.
- foreach my $column (@{$index_exists->{FIELDS}}) {
- $self->bz_drop_related_fks($table, $column);
+ if ($self->INDEX_DROPS_REQUIRE_FK_DROPS) {
+ # We cannot delete an index used by a FK.
+ foreach my $column (@{$index_exists->{FIELDS}}) {
+ $self->bz_drop_related_fks($table, $column);
+ }
}
$self->bz_drop_index_raw($table, $name);
$self->_bz_real_schema->delete_index($table, $name);
diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm
index fb62965e3..33527c367 100644
--- a/Bugzilla/DB/Schema.pm
+++ b/Bugzilla/DB/Schema.pm
@@ -1000,7 +1000,7 @@ use constant ABSTRACT_SCHEMA => {
],
},
- tags => {
+ tag => {
FIELDS => [
id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
name => {TYPE => 'varchar(64)', NOTNULL => 1},
@@ -1010,7 +1010,7 @@ use constant ABSTRACT_SCHEMA => {
DELETE => 'CASCADE'}},
],
INDEXES => [
- tags_user_id_idx => {FIELDS => [qw(user_id name)], TYPE => 'UNIQUE'},
+ tag_user_id_idx => {FIELDS => [qw(user_id name)], TYPE => 'UNIQUE'},
],
},
@@ -1021,7 +1021,7 @@ use constant ABSTRACT_SCHEMA => {
COLUMN => 'bug_id',
DELETE => 'CASCADE'}},
tag_id => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'tags',
+ REFERENCES => {TABLE => 'tag',
COLUMN => 'id',
DELETE => 'CASCADE'}},
],
diff --git a/Bugzilla/DB/Schema/Sqlite.pm b/Bugzilla/DB/Schema/Sqlite.pm
index 4730c4f9f..aad1f17bc 100644
--- a/Bugzilla/DB/Schema/Sqlite.pm
+++ b/Bugzilla/DB/Schema/Sqlite.pm
@@ -162,6 +162,23 @@ sub get_create_database_sql {
die "Reached an unreachable point";
}
+sub _get_create_table_ddl {
+ my $self = shift;
+ my ($table) = @_;
+ my $ddl = $self->SUPER::_get_create_table_ddl(@_);
+
+ # TheSchwartz uses its own driver to access its tables, meaning
+ # that it doesn't understand "COLLATE bugzilla" and in fact
+ # SQLite throws an error when TheSchwartz tries to access its
+ # own tables, if COLLATE bugzilla is on them. We don't have
+ # to fix this elsewhere currently, because we only create
+ # TheSchwartz's tables, we never modify them.
+ if ($table =~ /^ts_/) {
+ $ddl =~ s/ COLLATE bugzilla//g;
+ }
+ return $ddl;
+}
+
sub get_type_ddl {
my $self = shift;
my $def = dclone($_[0]);
diff --git a/Bugzilla/DB/Sqlite.pm b/Bugzilla/DB/Sqlite.pm
index e40a264f0..fb6aaba97 100644
--- a/Bugzilla/DB/Sqlite.pm
+++ b/Bugzilla/DB/Sqlite.pm
@@ -25,6 +25,7 @@ use base qw(Bugzilla::DB);
use Bugzilla::Constants;
use Bugzilla::Error;
+use Bugzilla::Install::Util qw(install_string);
use DateTime;
use POSIX ();
@@ -39,6 +40,10 @@ use constant ISOLATION_LEVEL => undef;
use constant WORD_START => '(?:^|\W)';
use constant WORD_END => '(?:$|\W)';
+# For some reason, dropping the related FKs causes the index to
+# disappear early, which causes all sorts of problems.
+use constant INDEX_DROPS_REQUIRE_FK_DROPS => 0;
+
####################################
# Functions Added To SQLite Itself #
####################################
@@ -242,6 +247,46 @@ sub sql_string_until {
# bz_ methods #
###############
+sub bz_setup_database {
+ my $self = shift;
+ $self->SUPER::bz_setup_database(@_);
+
+ # If we created TheSchwartz tables with COLLATE bugzilla (during the
+ # 4.1.x development series) re-create them without it.
+ my @tables = $self->bz_table_list();
+ my @ts_tables = grep { /^ts_/ } @tables;
+ my $drop_ok;
+ foreach my $table (@ts_tables) {
+ my $create_table =
+ $self->_bz_real_schema->_sqlite_create_table($table);
+ if ($create_table =~ /COLLATE bugzilla/) {
+ if (!$drop_ok) {
+ _sqlite_jobqueue_drop_message();
+ $drop_ok = 1;
+ }
+ $self->bz_drop_table($table);
+ $self->bz_add_table($table);
+ }
+ }
+}
+
+sub _sqlite_jobqueue_drop_message {
+ # This is not translated because this situation will only happen if
+ # you are updating from a 4.1.x development version of Bugzilla using
+ # SQLite, and we don't want to maintain this string in strings.txt.pl
+ # forever for just this one uncommon circumstance.
+ print <<END;
+WARNING: We have to re-create all the database tables used by jobqueue.pl.
+If there are any pending jobs in the database (that is, emails that
+haven't been sent), they will be deleted.
+
+END
+ unless (Bugzilla->installation_answers->{NO_PAUSE}) {
+ print install_string('enter_or_ctrl_c');
+ getc;
+ }
+}
+
# XXX This needs to be implemented.
sub bz_explain { }
diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm
index af276882c..5ce3c7a4e 100644
--- a/Bugzilla/Install/DB.pm
+++ b/Bugzilla/Install/DB.pm
@@ -646,6 +646,8 @@ sub update_table_definitions {
$dbh->bz_add_column('bug_see_also', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
+ _rename_tags_to_tag();
+
# 2011-01-29 LpSolit@gmail.com - Bug 616185
_migrate_user_tags();
@@ -3485,9 +3487,9 @@ sub _migrate_user_tags {
WHERE query_type != 0');
my $sth_tags = $dbh->prepare(
- 'INSERT INTO tags (user_id, name) VALUES (?, ?)');
+ 'INSERT INTO tag (user_id, name) VALUES (?, ?)');
my $sth_tag_id = $dbh->prepare(
- 'SELECT id FROM tags WHERE user_id = ? AND name = ?');
+ 'SELECT id FROM tag WHERE user_id = ? AND name = ?');
my $sth_bug_tag = $dbh->prepare('INSERT INTO bug_tag (bug_id, tag_id)
VALUES (?, ?)');
my $sth_nq = $dbh->prepare('UPDATE namedqueries SET query = ?
@@ -3586,6 +3588,20 @@ sub _migrate_disabledtext_boolean {
}
}
+sub _rename_tags_to_tag {
+ my $dbh = Bugzilla->dbh;
+ if ($dbh->bz_table_info('tags')) {
+ # If we get here, it's because the schema created "tag" as an empty
+ # table while "tags" still exists. We get rid of the empty
+ # tag table so we can do the rename over the top of it.
+ $dbh->bz_drop_table('tag');
+ $dbh->bz_drop_index('tags', 'tags_user_id_idx');
+ $dbh->bz_rename_table('tags','tag');
+ $dbh->bz_add_index('tag', 'tag_user_id_idx',
+ {FIELDS => [qw(user_id name)], TYPE => 'UNIQUE'});
+ }
+}
+
1;
__END__
diff --git a/Bugzilla/Migrate.pm b/Bugzilla/Migrate.pm
index 4d7637527..ee0dcab95 100644
--- a/Bugzilla/Migrate.pm
+++ b/Bugzilla/Migrate.pm
@@ -294,6 +294,7 @@ sub check_requirements {
my $missing = Bugzilla::Install::Requirements::_check_missing(
$self->REQUIRED_MODULES, 1);
my %results = (
+ apache => [],
pass => @$missing ? 0 : 1,
missing => $missing,
any_missing => @$missing ? 1 : 0,
diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm
index a5c3e032d..d47e0ae99 100644
--- a/Bugzilla/Search.pm
+++ b/Bugzilla/Search.pm
@@ -2525,8 +2525,8 @@ sub _multiselect_table {
" ON keywords.keywordid = keyworddefs.id";
}
elsif ($field eq 'tag') {
- $args->{full_field} = 'tags.name';
- return "bug_tag INNER JOIN tags ON bug_tag.tag_id = tags.id"
+ $args->{full_field} = 'tag.name';
+ return "bug_tag INNER JOIN tag ON bug_tag.tag_id = tag.id"
. " AND user_id = " . $self->_user->id;
}
elsif ($field eq 'bug_group') {
diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm
index 3a3edcb5b..ea186a0fd 100644
--- a/Bugzilla/User.pm
+++ b/Bugzilla/User.pm
@@ -423,8 +423,8 @@ sub tags {
# in which case there are no bugs with this tag yet.
$self->{tags} = $dbh->selectall_hashref(
'SELECT name, id, COUNT(bug_id) AS bug_count
- FROM tags
- LEFT JOIN bug_tag ON bug_tag.tag_id = tags.id
+ FROM tag
+ LEFT JOIN bug_tag ON bug_tag.tag_id = tag.id
WHERE user_id = ? ' . $dbh->sql_group_by('id', 'name'),
'name', undef, $self->id);
}
diff --git a/contrib/README b/contrib/README
index 32a5b6488..f4e40e4a3 100644
--- a/contrib/README
+++ b/contrib/README
@@ -8,19 +8,13 @@ This file is encoded in UTF8 for purposes of contributor names.
This directory includes:
-bugzilla_ldapsync.rb -- Script that can be run via Cron that queries an LDAP
- server for e-mail addresses to add Bugzilla users
- for. Will optionally disable Bugzilla users with
- no matching LDAP record. Contributed by Thomas
- Stromberg <thomas+bugzilla@stromberg.org>.
-
bugzilla-submit/ -- A standalone bug submission program.
bzdbcopy.pl -- A script to copy data from an installation running
on one DB platform to an installation running on
another DB platform.
-bz_webservice_demo.p -- An example script that demonstrates how to talk to
+bz_webservice_demo.pl -- An example script that demonstrates how to talk to
Bugzilla via XMLRPC.
cmdline/ -- Various commands for querying your Bugzilla
@@ -30,13 +24,6 @@ bz_webservice_demo.p -- An example script that demonstrates how to talk to
from a given directory. The log is useful when
changes need to be backed out.
- gnatsparse/ -- A Python script used to import a GNATS database
- into Bugzilla.
-
- gnats2bz.pl -- A Perl script to help import bugs from a GNATS
- database into a Bugzilla database. Contributed by
- Tom Schutter <tom@platte.com>.
-
jb2bz.py -- Script to import bugs from JitterBug to Bugzilla.
merge-users.pl -- Script to merge two user accounts. The activities
@@ -68,6 +55,3 @@ sendunsentbugmail.pl -- Script to find bugs with un-sent mail and to
missing users to Bugzilla. Can disable/update
non-existing/changed information. Contributed by
Andreas Höfler <andreas.hoefler@bearingpoint.com>.
-
- yp_nomail.sh -- Script that can be run via Cron that regularly updates
- the nomail file for terminated employees.
diff --git a/contrib/yp_nomail.sh b/contrib/yp_nomail.sh
deleted file mode 100755
index 9d23d5e33..000000000
--- a/contrib/yp_nomail.sh
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/bin/sh
-# -*- Mode: ksh -*-
-##############################################################################
-# yp_nomail
-#
-# Our mail admins got annoyed when bugzilla kept sending email
-# to people who'd had bugzilla entries and left the company. They
-# were no longer in the list of valid email users so it'd bounce.
-# Maintaining the 'data/nomail' file was a pain. Luckily, our UNIX
-# admins list all the users that ever were, but the people who've left
-# have a distinct marker in their password file. For example:
-#
-# fired:*LK*:2053:1010:You're Fired Dude:/home/loser:/bin/false
-#
-# This script takes advantage of the "*LK*" convention seen via
-# ypcat passwd and dumps those people into the nomail file. Any
-# manual additions are kept in a "nomail.(domainname)" file and
-# appended to the list of yp lockouts every night via Cron
-#
-# 58 23 * * * /export/bugzilla/contrib/yp_nomail.sh > /dev/null 2>&1
-#
-# Tak ( Mark Takacs ) 08/2000
-#
-# XXX: Maybe should crosscheck w/bugzilla users?
-##############################################################################
-
-####
-# Configure this section to suite yer installation
-####
-
-DOMAIN=`domainname`
-MOZILLA_HOME="/export/mozilla"
-BUGZILLA_HOME="${MOZILLA_HOME}/bugzilla"
-NOMAIL_DIR="${BUGZILLA_HOME}/data"
-NOMAIL="${NOMAIL_DIR}/nomail"
-NOMAIL_ETIME="${NOMAIL}.${DOMAIN}"
-NOMAIL_YP="${NOMAIL}.yp"
-FIRED_FLAG="\*LK\*"
-
-YPCAT="/usr/bin/ypcat"
-GREP="/usr/bin/grep"
-SORT="/usr/bin/sort"
-
-########################## no more config needed #################
-
-# This dir comes w/Bugzilla. WAY too paranoid
-if [ ! -d ${NOMAIL_DIR} ] ; then
- echo "Creating $date_dir"
- mkdir -p ${NOMAIL_DIR}
-fi
-
-#
-# Do some (more) paranoid checking
-#
-touch ${NOMAIL}
-if [ ! -w ${NOMAIL} ] ; then
- echo "Can't write nomail file: ${NOMAIL} -- exiting"
- exit
-fi
-if [ ! -r ${NOMAIL_ETIME} ] ; then
- echo "Can't access custom nomail file: ${NOMAIL_ETIME} -- skipping"
- NOMAIL_ETIME=""
-fi
-
-#
-# add all the people with '*LK*' password to the nomail list
-# XXX: maybe I should customize the *LK* string. Doh.
-#
-
-LOCKOUT=`$YPCAT passwd | $GREP "${FIRED_FLAG}" | cut -d: -f1 | sort > ${NOMAIL_YP}`
-`cat ${NOMAIL_YP} ${NOMAIL_ETIME} > ${NOMAIL}`
-
-exit
-
-
-# end
-
diff --git a/editusers.cgi b/editusers.cgi
index 2efdd01c8..c25c5e9ef 100755
--- a/editusers.cgi
+++ b/editusers.cgi
@@ -74,7 +74,7 @@ if ($action eq 'search') {
###########################################################################
} elsif ($action eq 'list') {
my $matchvalue = $cgi->param('matchvalue') || '';
- my $matchstr = $cgi->param('matchstr');
+ my $matchstr = trim($cgi->param('matchstr'));
my $matchtype = $cgi->param('matchtype');
my $grouprestrict = $cgi->param('grouprestrict') || '0';
my $query = 'SELECT DISTINCT userid, login_name, realname, is_enabled ' .
@@ -137,7 +137,7 @@ if ($action eq 'search') {
$expr = "profiles.login_name";
}
- if ($matchstr =~ /^(regexp|notregexp|exact)$/) {
+ if ($matchtype =~ /^(regexp|notregexp|exact)$/) {
$matchstr ||= '.';
}
else {
diff --git a/query.cgi b/query.cgi
index 93de62b63..43649cc58 100755
--- a/query.cgi
+++ b/query.cgi
@@ -139,6 +139,9 @@ my %components;
my %versions;
my %milestones;
+# Exclude products with no components.
+@selectable_products = grep { scalar @{$_->components} } @selectable_products;
+
foreach my $product (@selectable_products) {
$components{$_->name} = 1 foreach (@{$product->components});
$versions{$_->name} = 1 foreach (@{$product->versions});
diff --git a/t/004template.t b/t/004template.t
index d38ae37e2..3b858c0b3 100644
--- a/t/004template.t
+++ b/t/004template.t
@@ -38,8 +38,7 @@ use CGI qw(-no_debug);
use File::Spec;
use Template;
-use Test::More tests => ( scalar(@referenced_files) * scalar(@languages)
- + $num_actual_files );
+use Test::More tests => ( scalar(@referenced_files) + $num_actual_files );
# Capture the TESTOUT from Test::More or Test::Builder for printing errors.
# This will handle verbosity for us automatically.
@@ -55,24 +54,17 @@ my $fh;
}
}
-# Checks whether one of the passed files exists
-sub existOnce {
- foreach my $file (@_) {
- return $file if -e $file;
- }
- return 0;
-}
-
-# Check to make sure all templates that are referenced in
-# Bugzilla exist in the proper place.
+# Check to make sure all templates that are referenced in Bugzilla
+# exist in the proper place in the English template directory.
+# All other languages may or may not include any template as Bugzilla will
+# fall back to English if necessary.
foreach my $file (@referenced_files) {
- my @path = map(File::Spec->catfile($_, $file), @include_paths);
- if (my $path = existOnce(@path)) {
+ my $path = File::Spec->catfile($english_default_include_path, $file);
+ if (-e $path) {
ok(1, "$path exists");
} else {
- ok(0, "$file cannot be located --ERROR");
- print $fh "Looked in:\n " . join("\n ", @path) . "\n";
+ ok(0, "$path cannot be located --ERROR");
}
}
@@ -114,19 +106,17 @@ foreach my $include_path (@include_paths) {
foreach my $file (@{$actual_files{$include_path}}) {
my $path = File::Spec->catfile($include_path, $file);
- if (-e $path) {
- my ($data, $err) = $provider->fetch($file);
-
- if (!$err) {
- ok(1, "$file syntax ok");
- }
- else {
- ok(0, "$file has bad syntax --ERROR");
- print $fh $data . "\n";
- }
+
+ # These are actual files, so there's no need to check for existence.
+
+ my ($data, $err) = $provider->fetch($file);
+
+ if (!$err) {
+ ok(1, "$path syntax ok");
}
else {
- ok(1, "$path doesn't exist, skipping test");
+ ok(0, "$path has bad syntax --ERROR");
+ print $fh $data . "\n";
}
}
}
diff --git a/t/Support/Templates.pm b/t/Support/Templates.pm
index 90b63a726..81dc8cc3f 100644
--- a/t/Support/Templates.pm
+++ b/t/Support/Templates.pm
@@ -29,12 +29,13 @@ use strict;
use lib 't';
use base qw(Exporter);
@Support::Templates::EXPORT =
- qw(@languages @include_paths %include_path @referenced_files
- %actual_files $num_actual_files);
-use vars qw(@languages @include_paths %include_path @referenced_files
- %actual_files $num_actual_files);
+ qw(@languages @include_paths $english_default_include_path
+ %include_path @referenced_files %actual_files $num_actual_files);
+use vars qw(@languages @include_paths $english_default_include_path
+ %include_path @referenced_files %actual_files $num_actual_files);
use Bugzilla;
+use Bugzilla::Constants;
use Bugzilla::Install::Util qw(template_include_path);
use Support::Files;
@@ -50,6 +51,10 @@ use File::Spec;
# All include paths
@include_paths = ();
+# English default include path
+$english_default_include_path =
+ File::Spec->catdir(bz_locations()->{'templatedir'}, 'en', 'default');
+
# Files which are referenced in the cgi files
@referenced_files = ();
diff --git a/template/en/default/bug/field-help.none.tmpl b/template/en/default/bug/field-help.none.tmpl
index de16f20ad..7ae9991ec 100644
--- a/template/en/default/bug/field-help.none.tmpl
+++ b/template/en/default/bug/field-help.none.tmpl
@@ -95,7 +95,7 @@ keywords =>
longdesc =>
"$terms.Bugs have comments added to them by $terms.Bugzilla users."
- _ "You can search for some text in those comments.",
+ _ " You can search for some text in those comments.",
op_sys =>
"The operating system the $terms.bug was observed on.",
diff --git a/template/en/default/global/field-descs.none.tmpl b/template/en/default/global/field-descs.none.tmpl
index 130ad82c6..21f41c86c 100644
--- a/template/en/default/global/field-descs.none.tmpl
+++ b/template/en/default/global/field-descs.none.tmpl
@@ -133,7 +133,7 @@
"settings" => "Settings",
"short_desc" => "Summary",
"status_whiteboard" => "Whiteboard",
- "tags.name" => "Tags",
+ "tag.name" => "Tags",
"target_milestone" => "Target Milestone",
"version" => "Version",
"work_time" => "Hours Worked",