summaryrefslogtreecommitdiffstats
path: root/Bugzilla
diff options
context:
space:
mode:
authorFrédéric Buclin <LpSolit@gmail.com>2011-01-30 13:07:59 +0100
committerFrédéric Buclin <LpSolit@gmail.com>2011-01-30 13:07:59 +0100
commit8fa9965e5476717e574f2674c6df8c4487874634 (patch)
tree91c01615dbb0b6d2b576c2311f578a0ae7c44b29 /Bugzilla
parent95bfc797b43bd7f1d8f45ea629aa6119b51e8a29 (diff)
downloadbugzilla-8fa9965e5476717e574f2674c6df8c4487874634.tar.gz
bugzilla-8fa9965e5476717e574f2674c6df8c4487874634.tar.xz
Bug 616185: Move tags (aka lists of bugs) to their own DB tables
r/a=mkanat
Diffstat (limited to 'Bugzilla')
-rw-r--r--Bugzilla/Bug.pm83
-rw-r--r--Bugzilla/Constants.pm7
-rw-r--r--Bugzilla/DB/Schema.pm31
-rw-r--r--Bugzilla/Install/DB.pm48
-rw-r--r--Bugzilla/Search/Saved.pm12
-rw-r--r--Bugzilla/User.pm23
6 files changed, 177 insertions, 27 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index 398843009..e516e4bf8 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -1895,6 +1895,17 @@ sub _check_strict_isolation_for_user {
}
}
+sub _check_tag_name {
+ my ($invocant, $tag) = @_;
+
+ $tag = clean_text($tag);
+ $tag || ThrowUserError('no_tag_to_edit');
+ ThrowUserError('tag_name_too_long') if length($tag) > MAX_LEN_QUERY_NAME;
+ trick_taint($tag);
+ # Tags are all lowercase.
+ return lc($tag);
+}
+
sub _check_target_milestone {
my ($invocant, $target, undef, $params) = @_;
my $product = blessed($invocant) ? $invocant->product_obj
@@ -2866,6 +2877,78 @@ sub remove_see_also {
$self->{see_also} = \@new_see_also;
}
+sub add_tag {
+ my ($self, $tag) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ $tag = $self->_check_tag_name($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 (?, ?)',
+ undef, ($user->id, $tag));
+
+ $tag_id = $dbh->selectrow_array('SELECT id FROM tags
+ WHERE name = ? AND user_id = ?',
+ undef, ($tag, $user->id));
+ # The list has changed.
+ delete $user->{tags};
+ }
+ # Do nothing if this tag is already set for this bug.
+ return if grep { $_ eq $tag } @{$self->tags};
+
+ # Increment the counter. Do it before the SQL call below,
+ # to not count the tag twice.
+ $user->tags->{$tag}->{bug_count}++;
+
+ $dbh->do('INSERT INTO bug_tag (bug_id, tag_id) VALUES (?, ?)',
+ undef, ($self->id, $tag_id));
+
+ push(@{$self->{tags}}, $tag);
+}
+
+sub remove_tag {
+ my ($self, $tag) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ $tag = $self->_check_tag_name($tag);
+
+ my $tag_id = exists $user->tags->{$tag} ? $user->tags->{$tag}->{id} : undef;
+ # Do nothing if the user doesn't use this tag, or didn't set it for this bug.
+ return unless ($tag_id && grep { $_ eq $tag } @{$self->tags});
+
+ $dbh->do('DELETE FROM bug_tag WHERE bug_id = ? AND tag_id = ?',
+ undef, ($self->id, $tag_id));
+
+ $self->{tags} = [grep { $_ ne $tag } @{$self->tags}];
+
+ # 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 = ?',
+ undef, ($tag, $user->id));
+
+ # The list has changed.
+ delete $user->{tags};
+ }
+}
+
+sub tags {
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+
+ # This method doesn't support several users using the same bug object.
+ if (!exists $self->{tags}) {
+ $self->{tags} = $dbh->selectcol_arrayref(
+ 'SELECT name FROM bug_tag
+ INNER JOIN tags ON tags.id = bug_tag.tag_id
+ WHERE bug_id = ? AND user_id = ?',
+ undef, ($self->id, $user->id));
+ }
+ return $self->{tags};
+}
+
#####################################################################
# Simple Accessors
#####################################################################
diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm
index f25c4f156..5b58fd494 100644
--- a/Bugzilla/Constants.pm
+++ b/Bugzilla/Constants.pm
@@ -82,9 +82,6 @@ use Memoize;
DEFAULT_QUERY_NAME
DEFAULT_MILESTONE
- QUERY_LIST
- LIST_OF_BUGS
-
SAVE_NUM_SEARCHES
COMMENT_COLS
@@ -288,10 +285,6 @@ use constant DEFAULT_QUERY_NAME => '(Default query)';
# The default "defaultmilestone" created for products.
use constant DEFAULT_MILESTONE => '---';
-# The possible types for saved searches.
-use constant QUERY_LIST => 0;
-use constant LIST_OF_BUGS => 1;
-
# How many of the user's most recent searches to save.
use constant SAVE_NUM_SEARCHES => 10;
diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm
index e2c1d22e4..2e1b3f78a 100644
--- a/Bugzilla/DB/Schema.pm
+++ b/Bugzilla/DB/Schema.pm
@@ -953,7 +953,6 @@ use constant ABSTRACT_SCHEMA => {
DELETE => 'CASCADE'}},
name => {TYPE => 'varchar(64)', NOTNULL => 1},
query => {TYPE => 'LONGTEXT', NOTNULL => 1},
- query_type => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0},
],
INDEXES => [
namedqueries_userid_idx => {FIELDS => [qw(userid name)],
@@ -979,6 +978,36 @@ use constant ABSTRACT_SCHEMA => {
],
},
+ tags => {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
+ name => {TYPE => 'varchar(64)', NOTNULL => 1},
+ user_id => {TYPE => 'INT3', NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles',
+ COLUMN => 'userid',
+ DELETE => 'CASCADE'}},
+ ],
+ INDEXES => [
+ tags_user_id_idx => {FIELDS => [qw(user_id name)], TYPE => 'UNIQUE'},
+ ],
+ },
+
+ bug_tag => {
+ FIELDS => [
+ bug_id => {TYPE => 'INT3', NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs',
+ COLUMN => 'bug_id',
+ DELETE => 'CASCADE'}},
+ tag_id => {TYPE => 'INT3', NOTNULL => 1,
+ REFERENCES => {TABLE => 'tags',
+ COLUMN => 'id',
+ DELETE => 'CASCADE'}},
+ ],
+ INDEXES => [
+ bug_tag_bug_id_idx => {FIELDS => [qw(bug_id tag_id)], TYPE => 'UNIQUE'},
+ ],
+ },
+
component_cc => {
FIELDS => [
diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm
index fee87fa92..7233c9dc0 100644
--- a/Bugzilla/Install/DB.pm
+++ b/Bugzilla/Install/DB.pm
@@ -438,10 +438,6 @@ sub update_table_definitions {
# PUBLIC is a reserved word in Oracle.
$dbh->bz_rename_column('series', 'public', 'is_public');
- # 2005-10-21 LpSolit@gmail.com - Bug 313020
- $dbh->bz_add_column('namedqueries', 'query_type',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
-
# 2005-11-04 LpSolit@gmail.com - Bug 305927
$dbh->bz_alter_column('groups', 'userregexp',
{TYPE => 'TINYTEXT', NOTNULL => 1, DEFAULT => "''"});
@@ -549,10 +545,6 @@ sub update_table_definitions {
# 2007-09-09 LpSolit@gmail.com - Bug 99215
_fix_attachment_modification_date();
- # This had the wrong definition in DB::Schema.
- $dbh->bz_alter_column('namedqueries', 'query_type',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
-
$dbh->bz_drop_index('longdescs', 'longdescs_thetext_idx');
_populate_bugs_fulltext();
@@ -650,6 +642,9 @@ sub update_table_definitions {
$dbh->bz_add_column('bug_see_also', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
+ # 2011-01-29 LpSolit@gmail.com - Bug 616185
+ _migrate_user_tags();
+
################################################################
# New --TABLE-- changes should go *** A B O V E *** this point #
################################################################
@@ -3472,6 +3467,43 @@ sub _fix_series_indexes {
{FIELDS => [qw(category subcategory name)], TYPE => 'UNIQUE'});
}
+sub _migrate_user_tags {
+ my $dbh = Bugzilla->dbh;
+ return unless $dbh->bz_column_info('namedqueries', 'query_type');
+
+ my $tags = $dbh->selectall_arrayref('SELECT userid, name, query
+ FROM namedqueries
+ WHERE query_type != 0');
+
+ my $sth_tags = $dbh->prepare('INSERT INTO tags (user_id, name) VALUES (?, ?)');
+ my $sth_bug_tag = $dbh->prepare('INSERT INTO bug_tag (bug_id, tag_id)
+ VALUES (?, ?)');
+ my $sth_nq = $dbh->prepare('UPDATE namedqueries SET query = ?
+ WHERE userid = ? AND name = ?');
+
+ foreach my $tag (@$tags) {
+ my ($user_id, $name, $query) = @$tag;
+ # Tags are all lowercase.
+ my $tag_name = lc($name);
+
+ $sth_tags->execute($user_id, $tag_name);
+ my $tag_id = $dbh->selectrow_array(
+ 'SELECT id FROM tags WHERE user_id = ? AND name = ?',
+ undef, ($user_id, $tag_name));
+
+ $query =~ s/^bug_id=//;
+ my @bug_ids = split(/[\s,]+/, $query);
+ $sth_bug_tag->execute($_, $tag_id) foreach @bug_ids;
+
+ # Existing tags may be used in whines, or shared with
+ # other users. So we convert them rather than delete them.
+ my $encoded_name = url_quote($tag_name);
+ $sth_nq->execute("tag=$encoded_name", $user_id, $name);
+ }
+
+ $dbh->bz_drop_column('namedqueries', 'query_type');
+}
+
1;
__END__
diff --git a/Bugzilla/Search/Saved.pm b/Bugzilla/Search/Saved.pm
index 4b46fc75c..9828d6e02 100644
--- a/Bugzilla/Search/Saved.pm
+++ b/Bugzilla/Search/Saved.pm
@@ -45,17 +45,15 @@ use constant DB_COLUMNS => qw(
userid
name
query
- query_type
);
use constant VALIDATORS => {
name => \&_check_name,
query => \&_check_query,
- query_type => \&_check_query_type,
link_in_footer => \&_check_link_in_footer,
};
-use constant UPDATE_COLUMNS => qw(name query query_type);
+use constant UPDATE_COLUMNS => qw(name query);
###############
# Constructor #
@@ -141,12 +139,6 @@ sub _check_query {
return $cgi->query_string;
}
-sub _check_query_type {
- my ($invocant, $type) = @_;
- # Right now the only query type is LIST_OF_BUGS.
- return $type ? LIST_OF_BUGS : QUERY_LIST;
-}
-
#########################
# Database Manipulation #
#########################
@@ -301,7 +293,6 @@ sub shared_with_users {
# Simple Accessors #
####################
-sub type { return $_[0]->{'query_type'}; }
sub url { return $_[0]->{'query'}; }
sub user {
@@ -317,7 +308,6 @@ sub user {
sub set_name { $_[0]->set('name', $_[1]); }
sub set_url { $_[0]->set('query', $_[1]); }
-sub set_query_type { $_[0]->set('query_type', $_[1]); }
1;
diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm
index eafda6563..0b639ee0d 100644
--- a/Bugzilla/User.pm
+++ b/Bugzilla/User.pm
@@ -363,6 +363,24 @@ sub queries_available {
return $self->{queries_available};
}
+sub tags {
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ if (!defined $self->{tags}) {
+ # We must use LEFT JOIN instead of INNER JOIN as we may be
+ # in the process of inserting a new tag to some bugs,
+ # 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
+ WHERE user_id = ? ' . $dbh->sql_group_by('id', 'name'),
+ 'name', undef, $self->id);
+ }
+ return $self->{tags};
+}
+
##########################
# Saved Recent Bug Lists #
##########################
@@ -2074,6 +2092,11 @@ internally, such code must call this method to flush the cached result.
An arrayref of group ids. The user can share their own queries with these
groups.
+=item C<tags>
+
+Returns a hashref with tag IDs as key, and a hashref with tag 'id',
+'name' and 'bug_count' as value.
+
=back
=head2 Account Lockout