Commit f0c76112 authored by wurblzap%gmail.com's avatar wurblzap%gmail.com

Bug 69000: Permit a stored query to be marked "shared" and accessible by other users.

Patch by Marc Schumann <wurblzap@gmail.com>, r=vladd, a=myk
parent 09495a11
......@@ -79,6 +79,14 @@ sub get_param_list {
},
{
name => 'querysharegroup',
type => 's',
choices => \&_get_all_group_names,
default => 'editbugs',
checker => \&check_group
},
{
name => 'usevisibilitygroups',
type => 'b',
default => 0
......
......@@ -666,9 +666,10 @@ use constant ABSTRACT_SCHEMA => {
namedqueries => {
FIELDS => [
id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
PRIMARYKEY => 1},
userid => {TYPE => 'INT3', NOTNULL => 1},
name => {TYPE => 'varchar(64)', NOTNULL => 1},
linkinfooter => {TYPE => 'BOOLEAN', NOTNULL => 1},
query => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
query_type => {TYPE => 'BOOLEAN', NOTNULL => 1},
],
......@@ -678,6 +679,18 @@ use constant ABSTRACT_SCHEMA => {
],
},
namedqueries_link_in_footer => {
FIELDS => [
namedquery_id => {TYPE => 'INT3', NOTNULL => 1},
user_id => {TYPE => 'INT3', NOTNULL => 1},
],
INDEXES => [
namedqueries_link_in_footer_id_idx => {FIELDS => [qw(namedquery_id user_id)],
TYPE => 'UNIQUE'},
namedqueries_link_in_footer_userid_idx => ['user_id'],
],
},
# Authentication
# --------------
......@@ -806,6 +819,20 @@ use constant ABSTRACT_SCHEMA => {
],
},
# This table determines which groups a user must be a member of
# in order to see a named query somebody else shares.
namedquery_group_map => {
FIELDS => [
namedquery_id => {TYPE => 'INT3', NOTNULL => 1},
group_id => {TYPE => 'INT3', NOTNULL => 1},
],
INDEXES => [
namedquery_group_map_namedquery_id_idx =>
{FIELDS => [qw(namedquery_id)], TYPE => 'UNIQUE'},
namedquery_group_map_group_id_idx => ['group_id'],
],
},
category_group_map => {
FIELDS => [
category_id => {TYPE => 'INT2', NOTNULL => 1},
......
......@@ -204,25 +204,46 @@ sub queries {
return [] unless $self->id;
my $dbh = Bugzilla->dbh;
my $used_in_whine_ref = $dbh->selectcol_arrayref(q{
my $used_in_whine_ref = $dbh->selectall_hashref('
SELECT DISTINCT query_name
FROM whine_events we
INNER JOIN whine_queries wq
ON we.id = wq.eventid
WHERE we.owner_userid = ?}, undef, $self->{id});
my $queries_ref = $dbh->selectall_arrayref(q{
SELECT name, query, linkinfooter, query_type
FROM namedqueries
WHERE userid = ?
ORDER BY UPPER(name)},{'Slice'=>{}}, $self->{id});
foreach my $name (@$used_in_whine_ref) {
foreach my $queries_hash (@$queries_ref) {
if ($queries_hash->{name} eq $name) {
$queries_hash->{usedinwhine} = 1;
last;
}
WHERE we.owner_userid = ?',
'query_name', undef, $self->id);
# If the user is in any group, there may be shared queries to be included.
my $or_nqgm_group_id_in_usergroups = '';
if ($self->groups_as_string) {
$or_nqgm_group_id_in_usergroups =
'OR MAX(nqgm.group_id) IN (' . $self->groups_as_string . ') ';
}
my $queries_ref = $dbh->selectall_arrayref('
SELECT nq.id, MAX(userid) AS userid, name, query, query_type,
MAX(nqgm.group_id) AS shared_with_group,
COUNT(nql.namedquery_id) AS link_in_footer
FROM namedqueries AS nq
LEFT JOIN namedquery_group_map nqgm
ON nqgm.namedquery_id = nq.id
LEFT JOIN namedqueries_link_in_footer AS nql
ON nql.namedquery_id = nq.id
AND nql.user_id = ? ' .
$dbh->sql_group_by('nq.id', 'name, query, query_type') .
' HAVING MAX(nq.userid) = ? ' .
$or_nqgm_group_id_in_usergroups .
' ORDER BY UPPER(name)',
{'Slice'=>{}}, $self->id, $self->id);
foreach my $queries_hash (@$queries_ref) {
# For each query, determine whether it's being used in a whine.
if (exists($$used_in_whine_ref{$queries_hash->{'name'}})) {
$queries_hash->{'usedinwhine'} = 1;
}
# For shared queries, provide the sharer's user object.
if ($queries_hash->{'userid'} != $self->id) {
$queries_hash->{'user'} = new Bugzilla::User($queries_hash->{'userid'});
}
}
$self->{queries} = $queries_ref;
......@@ -660,6 +681,24 @@ sub visible_groups_as_string {
return join(', ', @{$self->visible_groups_inherited()});
}
# This function defines the groups a user may share a query with.
# More restrictive sites may want to build this reference to a list of group IDs
# from bless_groups instead of mirroring visible_groups_inherited, perhaps.
sub queryshare_groups {
my $self = shift;
if ($self->in_group(Bugzilla->params->{'querysharegroup'})) {
return $self->visible_groups_inherited();
}
else {
return [];
}
}
sub queryshare_groups_as_string {
my $self = shift;
return join(', ', @{$self->queryshare_groups()});
}
sub derive_regexp_groups {
my ($self) = @_;
......@@ -734,8 +773,8 @@ sub can_bless {
}
# Otherwise, we're checking a specific group
my $group_name = shift;
return (grep {$$_{'name'} eq $group_name} (@{$self->bless_groups})) ? 1 : 0;
my $group_id = shift;
return (grep {$$_{'id'} eq $group_id} (@{$self->bless_groups})) ? 1 : 0;
}
sub flatten_group_membership {
......@@ -1576,12 +1615,20 @@ Should only be called by C<Bugzilla::Auth::login>, for the most part.
=item C<queries>
Returns an array of the user's named queries, sorted in a case-insensitive
order by name. Each entry is a hash with three keys:
order by name. Each entry is a hash with five keys:
=over
=item *
id - The ID of the query
=item *
userid - The query owner's user ID
=item *
name - The name of the query
=item *
......@@ -1590,7 +1637,7 @@ query - The text for the query
=item *
linkinfooter - Whether or not the query should be displayed in the footer.
link_in_footer - Whether or not the query should be displayed in the footer.
=back
......@@ -1783,7 +1830,7 @@ When called with no arguments:
Returns C<1> if the user can bless at least one group, returns C<0> otherwise.
When called with one argument:
Returns C<1> if the user can bless the group with that name, returns
Returns C<1> if the user can bless the group with that id, returns
C<0> otherwise.
=item C<wants_bug_mail>
......
......@@ -203,18 +203,42 @@ sub DiffDate {
}
sub LookupNamedQuery {
my ($name) = @_;
my ($name, $sharer_id) = @_;
my $user = Bugzilla->login(LOGIN_REQUIRED);
my $dbh = Bugzilla->dbh;
# $name is safe -- we only use it below in a SELECT placeholder and then
# in error messages (which are always HTML-filtered).
my $owner_id;
# $name and $sharer_id are safe -- we only use them below in SELECT
# placeholders and then in error messages (which are always HTML-filtered).
$name || ThrowUserError("query_name_missing");
trick_taint($name);
my $result = $dbh->selectrow_array("SELECT query FROM namedqueries"
. " WHERE userid = ? AND name = ?"
, undef, ($user->id, $name));
if ($sharer_id) {
trick_taint($sharer_id);
$owner_id = $sharer_id;
}
else {
$owner_id = $user->id;
}
my ($id, $result) = $dbh->selectrow_array('SELECT id, query
FROM namedqueries
WHERE userid = ? AND name = ?',
undef, ($owner_id, $name));
defined($result)
|| ThrowUserError("missing_query", {'queryname' => $name,
'sharer_id' => $sharer_id});
if ($sharer_id) {
my $group = $dbh->selectrow_array('SELECT group_id
FROM namedquery_group_map
WHERE namedquery_id = ?',
undef, $id);
if (!grep {$_ == $group} values(%{$user->groups()})) {
ThrowUserError("missing_query", {'queryname' => $name,
'sharer_id' => $sharer_id});
}
}
defined($result) || ThrowUserError("missing_query", {'queryname' => $name});
$result
|| ThrowUserError("buglist_parameters_required", {'queryname' => $name});
......@@ -265,7 +289,8 @@ sub InsertNamedQuery {
# it when we display it to the user.
trick_taint($query);
$dbh->bz_lock_tables('namedqueries WRITE');
$dbh->bz_lock_tables('namedqueries WRITE',
'namedqueries_link_in_footer WRITE');
my $result = $dbh->selectrow_array("SELECT userid FROM namedqueries"
. " WHERE userid = ? AND name = ?"
......@@ -273,15 +298,22 @@ sub InsertNamedQuery {
if ($result) {
$query_existed_before = 1;
$dbh->do("UPDATE namedqueries"
. " SET query = ?, linkinfooter = ?, query_type = ?"
. " SET query = ?, query_type = ?"
. " WHERE userid = ? AND name = ?"
, undef, ($query, $link_in_footer, $query_type, $userid, $query_name));
, undef, ($query, $query_type, $userid, $query_name));
} else {
$query_existed_before = 0;
$dbh->do("INSERT INTO namedqueries"
. " (userid, name, query, linkinfooter, query_type)"
. " VALUES (?, ?, ?, ?, ?)"
, undef, ($userid, $query_name, $query, $link_in_footer, $query_type));
. " (userid, name, query, query_type)"
. " VALUES (?, ?, ?, ?)"
, undef, ($userid, $query_name, $query, $query_type));
if ($link_in_footer) {
$dbh->do('INSERT INTO namedqueries_link_in_footer
(namedquery_id, user_id)
VALUES (?, ?)',
undef,
($dbh->bz_last_key('namedqueries', 'id'), $userid));
}
}
$dbh->bz_unlock_tables();
......@@ -373,9 +405,15 @@ if ($cgi->param('cmdtype') eq "dorem" && $cgi->param('remaction') =~ /^run/) {
# Take appropriate action based on user's request.
if ($cgi->param('cmdtype') eq "dorem") {
if ($cgi->param('remaction') eq "run") {
$buffer = LookupNamedQuery(scalar $cgi->param("namedcmd"));
$vars->{'searchname'} = $cgi->param('namedcmd');
$vars->{'searchtype'} = "saved";
$buffer = LookupNamedQuery(scalar $cgi->param("namedcmd"),
scalar $cgi->param('sharer_id'));
# If this is the user's own query, remember information about it
# so that it can be modified easily.
if (!$cgi->param('sharer_id') ||
$cgi->param('sharer_id') == Bugzilla->user->id) {
$vars->{'searchname'} = $cgi->param('namedcmd');
$vars->{'searchtype'} = "saved";
}
$params = new Bugzilla::CGI($buffer);
$order = $params->param('order') || $order;
......@@ -415,9 +453,24 @@ if ($cgi->param('cmdtype') eq "dorem") {
}
# If we are here, then we can safely remove the saved search
$dbh->do("DELETE FROM namedqueries"
. " WHERE userid = ? AND name = ?"
, undef, ($user->id, $qname));
my ($query_id) = $dbh->selectrow_array('SELECT id FROM namedqueries
WHERE userid = ?
AND name = ?',
undef, ($user->id, $qname));
if (!$query_id) {
# The user has no query of this name. Play along.
}
else {
$dbh->do('DELETE FROM namedqueries
WHERE id = ?',
undef, $query_id);
$dbh->do('DELETE FROM namedqueries_link_in_footer
WHERE namedquery_id = ?',
undef, $query_id);
$dbh->do('DELETE FROM namedquery_group_map
WHERE namedquery_id = ?',
undef, $query_id);
}
# Now reset the cached queries
$user->flush_queries_cache();
......@@ -425,7 +478,7 @@ if ($cgi->param('cmdtype') eq "dorem") {
print $cgi->header();
# Generate and return the UI (HTML page) from the appropriate template.
$vars->{'message'} = "buglist_query_gone";
$vars->{'namedcmd'} = $cgi->param('namedcmd');
$vars->{'namedcmd'} = $qname;
$vars->{'url'} = "query.cgi";
$template->process("global/message.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
......
......@@ -2319,8 +2319,6 @@ if (!$dbh->bz_index_info('profiles', 'profiles_login_name_idx') ||
$dbh->bz_add_column('profiles', 'mybugslink', {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'TRUE'});
$dbh->bz_add_column('namedqueries', 'linkinfooter',
{TYPE => 'BOOLEAN', NOTNULL => 1}, 0);
my $comp_init_owner = $dbh->bz_column_info('components', 'initialowner');
if ($comp_init_owner && $comp_init_owner->{TYPE} eq 'TINYTEXT') {
......@@ -4369,6 +4367,22 @@ if (-e "$datadir/versioncache") {
unlink "$datadir/versioncache";
}
# 2006-07-01 wurblzap@gmail.com -- Bug 69000
$dbh->bz_add_column('namedqueries', 'id',
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
if ($dbh->bz_column_info("namedqueries", "linkinfooter")) {
# Move link-in-footer information into a table of its own.
my $sth_read = $dbh->prepare('SELECT id, userid
FROM namedqueries
WHERE linkinfooter = 1');
my $sth_write = $dbh->prepare('INSERT INTO namedqueries_link_in_footer
(namedquery_id, user_id) VALUES (?, ?)');
$sth_read->execute();
while (my ($id, $userid) = $sth_read->fetchrow_array()) {
$sth_write->execute($id, $userid);
}
$dbh->bz_drop_column("namedqueries", "linkinfooter");
}
# If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment.
......
......@@ -345,8 +345,20 @@
returns bugs where the content of the field matches any one of the selected
values. If none is selected, then the field can take any value.</para>
<para>Once you've run a search, you can save it as a Saved Search, which
appears in the page footer.</para>
<para>
Once you've run a search, you can save it as a Saved Search, which
appears in the page footer.
On the Saved Searches tab of your User Preferences page (the Prefs link
in Bugzilla's footer), members of the group defined in the
querysharegroup parameter can share such Saved Searches with user groups
so that other users may use them.
At the same place, you can see Saved Searches other users are sharing, and
have them show up in your personal Bugzilla footer along with your own
Saved Searches.
If somebody is sharing a Search with a group she or he is allowed to
<xref linkend="groups">assign users to</a>, it will show up in the
group's direct members' footers by default.
</para>
<section id="boolean">
<title>Boolean Charts</title>
......
......@@ -358,6 +358,12 @@ if ($action eq 'del') {
WHERE group_id IN ($grouplist) AND isbless = 0 " .
$dbh->sql_limit(1)) || 0;
my ($shared_queries) =
$dbh->selectrow_array('SELECT COUNT(*)
FROM namedquery_group_map
WHERE group_id = ?',
undef, $gid);
my $bug_ids = $dbh->selectcol_arrayref('SELECT bug_id FROM bug_group_map
WHERE group_id = ?', undef, $gid);
......@@ -372,14 +378,15 @@ if ($action eq 'del') {
$dbh->sql_limit(1),
undef, ($gid, $gid)) || 0;
$vars->{'gid'} = $gid;
$vars->{'name'} = $name;
$vars->{'description'} = $desc;
$vars->{'hasusers'} = $hasusers;
$vars->{'hasbugs'} = $hasbugs;
$vars->{'hasproduct'} = $hasproduct;
$vars->{'hasflags'} = $hasflags;
$vars->{'buglist'} = $buglist;
$vars->{'gid'} = $gid;
$vars->{'name'} = $name;
$vars->{'description'} = $desc;
$vars->{'hasusers'} = $hasusers;
$vars->{'hasbugs'} = $hasbugs;
$vars->{'hasproduct'} = $hasproduct;
$vars->{'hasflags'} = $hasflags;
$vars->{'shared_queries'} = $shared_queries;
$vars->{'buglist'} = $buglist;
print $cgi->header();
$template->process("admin/groups/delete.html.tmpl", $vars)
......@@ -462,6 +469,8 @@ if ($action eq 'delete') {
$dbh->do('UPDATE flagtypes SET request_group_id = ?
WHERE request_group_id = ?',
undef, (undef, $gid));
$dbh->do('DELETE FROM namedquery_group_map WHERE group_id = ?',
undef, $gid);
$dbh->do('DELETE FROM user_group_map WHERE group_id = ?',
undef, $gid);
$dbh->do('DELETE FROM group_group_map
......
......@@ -460,9 +460,18 @@ if ($action eq 'search') {
$vars->{'longdescs'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM longdescs WHERE who = ?',
undef, $otherUserID);
$vars->{'namedqueries'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM namedqueries WHERE userid = ?',
undef, $otherUserID);
$vars->{'namedqueries'} = $dbh->selectcol_arrayref(
'SELECT id FROM namedqueries WHERE userid = ?',
undef,
$otherUserID);
if (@{$vars->{'namedqueries'}}) {
$vars->{'namedquery_group_map'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM namedquery_group_map WHERE namedquery_id IN' .
' (' . join(', ', @{$vars->{'namedqueries'}}) . ')');
}
else {
$vars->{'namedquery_group_map'} = 0;
}
$vars->{'profile_setting'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM profile_setting WHERE user_id = ?',
undef, $otherUserID);
......@@ -525,6 +534,8 @@ if ($action eq 'search') {
'flagtypes READ',
'cc WRITE',
'namedqueries WRITE',
'namedqueries_link_in_footer WRITE',
'namedquery_group_map WRITE',
'tokens WRITE',
'votes WRITE',
'watch WRITE',
......@@ -545,6 +556,12 @@ if ($action eq 'search') {
Bugzilla->logout_user($otherUser);
# Get the named query list so we can delete namedquery_group_map entries.
my $namedqueries_as_string = join(', ', $dbh->selectcol_arrayref(
'SELECT id FROM namedqueries WHERE userid = ?',
undef,
$otherUserID));
# Get the timestamp for LogActivityEntry.
my $timestamp = $dbh->selectrow_array('SELECT NOW()');
......@@ -589,6 +606,12 @@ if ($action eq 'search') {
$otherUserID);
$dbh->do('DELETE FROM logincookies WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM namedqueries WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM namedqueries_link_in_footer WHERE user_id = ?', undef,
$otherUserID);
if ($namedqueries_as_string) {
$dbh->do('DELETE FROM namedquery_group_map WHERE namedquery_id IN ' .
"($namedqueries_as_string)");
}
$dbh->do('DELETE FROM profile_setting WHERE user_id = ?', undef,
$otherUserID);
$dbh->do('DELETE FROM profiles_activity WHERE userid = ? OR who = ?', undef,
......
......@@ -418,8 +418,14 @@ CrossCheck("groups", "id",
["group_group_map", "grantor_id"],
["group_group_map", "member_id"],
["group_control_map", "group_id"],
["namedquery_group_map", "group_id"],
["user_group_map", "group_id"]);
CrossCheck("namedqueries", "id",
["namedqueries_link_in_footer", "namedquery_id"],
["namedquery_group_map", "namedquery_id"],
);
CrossCheck("profiles", "userid",
['profiles_activity', 'userid'],
['profiles_activity', 'who'],
......@@ -438,6 +444,7 @@ CrossCheck("profiles", "userid",
["longdescs", "who", "bug_id"],
["logincookies", "userid"],
["namedqueries", "userid"],
["namedqueries_link_in_footer", "user_id"],
['series', 'creator', 'series_id', ['0']],
["watch", "watcher"],
["watch", "watched"],
......
......@@ -128,11 +128,6 @@ body
display: inline;
}
#footer span
{
display: block;
}
#footer .btn, #footer .txt
{
font-size: 0.8em;
......
......@@ -19,6 +19,13 @@
# Contributor(s): Gervase Markham <gerv@gerv.net>
#%]
[%# INTERFACE:
# queries: list of the named queries visible to the user, both own and shared
# by others. Cleaned-up result of Bugzilla::User::queries.
# queryshare_groups: list of groups the user may share queries with
# (id, name).
#%]
<p>Your saved searches are as follows:</p>
<blockquote>
......@@ -40,6 +47,17 @@
Show in
Footer
</th>
[% querysharegroup_regexp = '^' _ Param('querysharegroup') _ '$' %]
[% may_share = user.groups.keys.grep($querysharegroup_regexp).size %]
[% IF may_share %]
<th>
Share With
a Group
[% UNLESS queryshare_groups.size %]
(there are no groups you may share queries with)
[% END %]
</th>
[% END %]
</tr>
<tr>
<td>My Bugs</td>
......@@ -59,8 +77,12 @@
value="1"
[% " checked" IF user.showmybugslink %]>
</td>
<td>
&mdash;
</td>
</tr>
[% FOREACH q = queries %]
[% NEXT UNLESS q.userid == user.id %]
<tr>
<td>[% q.name FILTER html %]</td>
<td>
......@@ -79,9 +101,71 @@
</td>
<td align="center">
<input type="checkbox"
name="linkinfooter_[% q.name FILTER html %]"
name="link_in_footer_[% q.id FILTER html %]"
value="1"
[% " checked" IF q.link_in_footer %]>
</td>
<td>
[% IF queryshare_groups.size %]
<select name="share_[% q.id FILTER html %]">
<option value="">Don't share</option>
[% FOREACH group = queryshare_groups %]
<option value="[% group.id %]"
[% ' selected="selected"' IF q.shared_with_group == group.id %]>[% group.name FILTER html %]</option>
[% END %]
</select>
[% ELSE %]
&mdash;
[% END %]
</td>
</tr>
[% END %]
</table>
</blockquote>
<p>You may use these searches saved and shared by others:</p>
<blockquote>
<table border="1" cellpadding="3">
<tr>
<th>
Search
</th>
<th>
Shared By
</th>
<th>
Run
</th>
<th>
Show in
Footer
</th>
</tr>
[% found_shared_query = 0 %]
[% FOREACH q = queries %]
[% NEXT IF q.userid == user.id %]
[% found_shared_query = 1 %]
<tr>
<td>[% q.name FILTER html %]</td>
<td>[% q.user.identity FILTER html %]</td>
<td>
<a href="buglist.cgi?cmdtype=dorem&amp;remaction=run&amp;namedcmd=
[% q.name FILTER url_quote %]&amp;sharer_id=
[% q.userid FILTER url_quote %]">Run</a>
</td>
<td align="center">
<input type="checkbox"
name="link_in_footer_[% q.id FILTER html %]"
value="1"
[% " checked" IF q.linkinfooter %]>
[% " checked" IF q.link_in_footer %]>
</td>
</tr>
[% END %]
[% IF !found_shared_query %]
<tr>
<td colspan="4" style="text-align: center">
&lt;None&gt;
</td>
</tr>
[% END %]
......
......@@ -30,6 +30,7 @@
# hasbugs: boolean int. True if the group includes bugs in it.
# hasproduct: boolean int. True if the group is binded to a product.
# hasflags: boolean int. True if the group is used by a flag type.
# shared_queries: int. Number of saved searches being shared with this group.
# buglist: string. The list of bugs included in this group.
#%]
......@@ -92,6 +93,25 @@
flag types from this group for me.</p>
[% END %]
[% IF shared_queries %]
<p>
<b>There
[% IF shared_queries > 1 %]
are [% shared_queries %] saved searches
[% ELSE %]
is a saved search
[% END %]
being shared with this group.</b>
If you delete this group,
[% IF shared_queries > 1 %]
these saved searches
[% ELSE %]
this saved search
[% END %]
will fall back to being private again.
</p>
[% END %]
<h2>Confirmation</h2>
<p>Do you really want to delete this group?</p>
......
......@@ -46,6 +46,9 @@
timetrackinggroup => "The name of the group of users who can see/change time tracking " _
"information.",
querysharegroup => "The name of the group of users who can share their " _
"saved searches with others.",
usevisibilitygroups => "Do you wish to restrict visibility of users to members of " _
"specific groups?",
......
......@@ -31,7 +31,8 @@
# flags.requestee: number of flags the viewed user is being asked for
# flags.setter: number of flags the viewed user has set
# longdescs: number of bug comments the viewed user has written
# namedqueries: number of named queries the user has created
# namedqueries: array of IDs of named queries the user has created
# namedquery_group_map: number of named queries the user has shared
# profiles_activity: number of named queries the user has created
# series: number of series the viewed user has created
# votes: number of bugs the viewed user has voted on
......@@ -301,17 +302,35 @@
[% IF namedqueries %]
<li>
[% otheruser.login FILTER html %] has
[% IF namedqueries == 1 %]
a named query
[% IF namedqueries.size == 1 %]
a [% 'shared' IF namedquery_group_map %] named search
[% ELSE %]
[%+ namedqueries %] named queries
[%+ namedqueries.size %] named searches
[% END %].
[% IF namedqueries == 1 %]
This named query
[% IF namedqueries.size == 1 %]
This named search
[% ELSE %]
These named queries
These named searches
[% END %]
will be deleted along with the user account.
[% IF namedquery_group_map %]
[% IF namedqueries.size > 1 %]
Of these,
[% IF namedquery_group_map > 1 %]
[%+ namedquery_group_map FILTER html %] are
[% ELSE %]
one is
[% END %]
shared.
[% END %]
Other users will not be able to use
[% IF namedquery_group_map > 1 %]
these shared named searches
[% ELSE %]
this shared named search
[% END %]
any more.
[% END %]
</li>
[% END %]
[% IF profile_setting %]
......
......@@ -545,6 +545,10 @@
'comp.bug_count'
],
'admin/groups/delete.html.tmpl' => [
'shared_queries'
],
'admin/users/confirm-delete.html.tmpl' => [
'andstring',
'responsibilityterms.$responsibility',
......@@ -554,7 +558,6 @@
'flags.requestee',
'flags.setter',
'longdescs',
'namedqueries',
'votes',
'series',
'watch.watched',
......@@ -600,6 +603,10 @@
'current_tab.name',
],
'account/prefs/saved-searches.html.tmpl' => [
'group.id',
],
'account/prefs/settings.html.tmpl' => [
'name',
'default_name'
......
......@@ -18,6 +18,7 @@
#
# Contributor(s): Gervase Markham <gerv@gerv.net>
# Svetlana Harisova <light@rathedg.com>
# Marc Schumann <wurblzap@gmail.com>
#%]
[%# Migration note: this whole file corresponds to the old %commandmenu%
......@@ -67,23 +68,40 @@
<div id="links-saved">
<div class="label">
[% IF user.showmybugslink OR user.queries.size %]
Saved&nbsp;Searches:
Saved&nbsp;Searches:
[% END %]
</div>
<div class="links">
[% IF user.showmybugslink %]
[% filtered_username = user.login FILTER url_quote %]
<a href="[% Param('mybugstemplate').replace('%userid%', filtered_username) %]">My&nbsp;[% terms.Bugs %]</a>
[% IF user.showmybugslink %]
[% filtered_username = user.login FILTER url_quote %]
<a href="[% Param('mybugstemplate').replace('%userid%', filtered_username) %]">My&nbsp;[% terms.Bugs %]</a>
[% print_pipe = 1 %]
[% END %]
[% FOREACH q = user.queries %]
[% NEXT IF q.userid != user.id %]
[% IF q.link_in_footer %]
[% " | " IF print_pipe %]
<a href="buglist.cgi?cmdtype=dorem&amp;remaction=run&amp;namedcmd=
[% q.name FILTER url_quote %]">[% q.name FILTER html FILTER no_break %]</a>
[% print_pipe = 1 %]
[% END %]
[% END %]
[% FOREACH q = user.queries %]
[% IF q.linkinfooter %]
[% " | " IF print_pipe %]
<a href="buglist.cgi?cmdtype=runnamed&amp;namedcmd=[% q.name FILTER url_quote %]">[% q.name FILTER html FILTER no_break %]</a>
[% print_pipe = 1 %]
[% END %]
[% " <br> " IF print_pipe %]
[% print_pipe = 0 %]
[% FOREACH q = user.queries %]
[% NEXT IF q.userid == user.id %]
[% IF q.link_in_footer %]
[% " | " IF print_pipe %]
<a href="buglist.cgi?cmdtype=dorem&amp;remaction=run&amp;namedcmd=
[% q.name FILTER url_quote %]&amp;sharer_id=
[% q.userid FILTER url_quote %]"
class="shared"
title="Shared by [% q.user.identity FILTER html %]">[% q.name FILTER html FILTER no_break %]</a>
[% print_pipe = 1 %]
[% END %]
[% END %]
</div>
</div>
......
......@@ -920,8 +920,14 @@
[% ELSIF error == "missing_query" %]
[% title = "Missing Search" %]
The search named <em>[% queryname FILTER html %]</em> does not
exist.
[% docslinks = {'query.html' => "Searching for $terms.bugs",
'list.html' => "$terms.Bug lists"} %]
The search named <em>[% queryname FILTER html %]</em>
[% IF sharer_id %]
has not been made visible to you.
[% ELSE %]
does not exist.
[% END %]
[% ELSIF error == "move_bugs_disabled" %]
[% title = BLOCK %][% terms.Bug %] Moving Disabled[% END %]
......
......@@ -351,11 +351,12 @@ sub DoPermissions {
my ($nam, $desc) = @$group;
push(@has_bits, {"desc" => $desc, "name" => $nam});
}
$groups = $dbh->selectall_arrayref(
"SELECT DISTINCT name, description FROM groups ORDER BY name");
$groups = $dbh->selectall_arrayref('SELECT DISTINCT id, name, description
FROM groups
ORDER BY name');
foreach my $group (@$groups) {
my ($nam, $desc) = @$group;
if ($user->can_bless($nam)) {
my ($group_id, $nam, $desc) = @$group;
if ($user->can_bless($group_id)) {
push(@set_bits, {"desc" => $desc, "name" => $nam});
}
}
......@@ -370,6 +371,7 @@ sub DoPermissions {
sub DoSavedSearches {
# 2004-12-13 - colin.ogilvie@gmail.com, bug 274397
# Need to work around the possibly missing query_format=advanced
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
my @queries = @{$user->queries};
......@@ -391,6 +393,12 @@ sub DoSavedSearches {
push @newqueries, $q;
}
$vars->{'queries'} = \@newqueries;
if ($user->queryshare_groups_as_string) {
$vars->{'queryshare_groups'} = $dbh->selectall_arrayref(
'SELECT id, name FROM groups WHERE id IN ' .
'(' . $user->queryshare_groups_as_string .')',
{'Slice' => {}});
}
}
sub SaveSavedSearches {
......@@ -398,18 +406,114 @@ sub SaveSavedSearches {
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
# We'll need this in a loop, so do the call once.
my $user_id = $user->id;
my @queries = @{$user->queries};
my $sth = $dbh->prepare("UPDATE namedqueries SET linkinfooter = ?
WHERE userid = ?
AND name = ?");
my $sth_check_nl = $dbh->prepare('SELECT COUNT(*)
FROM namedqueries_link_in_footer
WHERE namedquery_id = ?
AND user_id = ?');
my $sth_insert_nl = $dbh->prepare('INSERT INTO namedqueries_link_in_footer
(namedquery_id, user_id)
VALUES (?, ?)');
my $sth_delete_nl = $dbh->prepare('DELETE FROM namedqueries_link_in_footer
WHERE namedquery_id = ?
AND user_id = ?');
my $sth_check_ngm = $dbh->prepare('SELECT COUNT(*)
FROM namedquery_group_map
WHERE namedquery_id = ?');
my $sth_insert_ngm = $dbh->prepare('INSERT INTO namedquery_group_map
(namedquery_id, group_id)
VALUES (?, ?)');
my $sth_update_ngm = $dbh->prepare('UPDATE namedquery_group_map
SET group_id = ?
WHERE namedquery_id = ?');
my $sth_delete_ngm = $dbh->prepare('DELETE FROM namedquery_group_map
WHERE namedquery_id = ?');
my $sth_direct_group_members =
$dbh->prepare('SELECT user_id
FROM user_group_map
WHERE group_id = ?
AND isbless = ' . GROUP_MEMBERSHIP . '
AND grant_type = ' . GRANT_DIRECT);
foreach my $q (@queries) {
my $linkinfooter =
defined($cgi->param("linkinfooter_$q->{'name'}")) ? 1 : 0;
$sth->execute($linkinfooter, $user->id, $q->{'name'});
# Update namedqueries_link_in_footer.
$sth_check_nl->execute($q->{'id'}, $user_id);
my ($link_in_footer_entries) = $sth_check_nl->fetchrow_array();
if (defined($cgi->param("link_in_footer_$q->{'id'}"))) {
if ($link_in_footer_entries == 0) {
$sth_insert_nl->execute($q->{'id'}, $user_id);
}
}
else {
if ($link_in_footer_entries > 0) {
$sth_delete_nl->execute($q->{'id'}, $user_id);
}
}
# For user's own queries, update namedquery_group_map.
if ($q->{'userid'} == $user_id) {
my ($group_id, $group_map_entries);
if ($user->in_group(Bugzilla->params->{'querysharegroup'})) {
$sth_check_ngm->execute($q->{'id'});
($group_map_entries) = $sth_check_ngm->fetchrow_array();
$group_id = $cgi->param("share_$q->{'id'}") || '';
}
if ($group_id) {
if (grep(/^\Q$group_id\E$/, @{$user->queryshare_groups()})) {
# $group_id is now definitely a valid ID of a group the
# user can see, so we can trick_taint.
trick_taint($group_id);
if ($group_map_entries == 0) {
$sth_insert_ngm->execute($q->{'id'}, $group_id);
}
else {
$sth_update_ngm->execute($group_id, $q->{'id'});
}
# If we're sharing our query with a group we can bless,
# we're subscribing direct group members to our search
# automatically. Otherwise, the group members need to
# opt in. This behaviour is deemed most likely to fit
# users' needs.
if ($user->can_bless($group_id)) {
$sth_direct_group_members->execute($group_id);
my $user_id;
while ($user_id =
$sth_direct_group_members->fetchrow_array()) {
next if $user_id == $user->id;
$sth_check_nl->execute($q->{'id'}, $user_id);
my ($already_shown_in_footer) =
$sth_check_nl->fetchrow_array();
if (! $already_shown_in_footer) {
$sth_insert_nl->execute($q->{'id'}, $user_id);
}
}
}
}
else {
# In the unlikely case somebody removed visibility to the
# group in the meantime, don't modify sharing.
}
}
else {
if ($group_map_entries > 0) {
$sth_delete_ngm->execute($q->{'id'});
}
# Don't remove namedqueries_link_in_footer entries for users
# subscribing to the shared query. The idea is that they will
# probably want to be subscribers again should the sharing
# user choose to share the query again.
}
}
}
$user->flush_queries_cache;
# Update profiles.mybugslink.
my $showmybugslink = defined($cgi->param("showmybugslink")) ? 1 : 0;
$dbh->do("UPDATE profiles SET mybugslink = ? WHERE userid = ?",
undef, ($showmybugslink, $user->id));
......
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