Add Group.{add,remove}_{members,managers} web services
Also move user_group_map manipulation code into Bugzilla::Groupclasses
parent
bd69dca4a1
commit
6e9ee91dcf
|
@ -373,6 +373,123 @@ sub remove_from_db
|
|||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
sub add_users
|
||||
{
|
||||
my $self = shift;
|
||||
my ($users, $isbless) = @_;
|
||||
return if !@$users;
|
||||
add_user_groups([ map { { group => $self, user => $_ } } @$users ], $isbless);
|
||||
}
|
||||
|
||||
sub remove_users
|
||||
{
|
||||
my $self = shift;
|
||||
my ($users, $isbless) = @_;
|
||||
return if !@$users;
|
||||
remove_user_groups([ map { { group => $self, user => $_ } } @$users ], $isbless);
|
||||
}
|
||||
|
||||
# Add members or blessers to this group
|
||||
# Bugzilla::Group::add_user_groups([ { user => Bugzilla::User or int id, group => Bugzilla::Group }, ... ], $isbless = 0 or 1)
|
||||
sub add_user_groups
|
||||
{
|
||||
shift if $_[0] eq __PACKAGE__;
|
||||
my ($rows, $isbless) = @_;
|
||||
return if !@$rows;
|
||||
# Filter duplicates
|
||||
my $g = {};
|
||||
for my $row (@$rows)
|
||||
{
|
||||
$g->{int(ref $row->{user} ? $row->{user}->id : $row->{user})}->{int($row->{group}->id)} = $row;
|
||||
}
|
||||
$isbless = $isbless ? 1 : 0;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
# Filter already existing members
|
||||
for my $row (@{ $dbh->selectall_arrayref(
|
||||
"SELECT user_id, group_id FROM user_group_map WHERE (user_id, group_id, grant_type, isbless) IN (".
|
||||
join(', ', map {
|
||||
my $uid = $_;
|
||||
map { "($uid, $_, ".GRANT_DIRECT.", $isbless)" } keys %{$g->{$uid}};
|
||||
} keys %$g).") FOR UPDATE", undef
|
||||
) || [] })
|
||||
{
|
||||
delete $g->{$row->[0]}->{$row->[1]};
|
||||
delete $g->{$row->[0]} if !%{$g->{$row->[0]}};
|
||||
}
|
||||
return if !%$g;
|
||||
# Apply update
|
||||
$dbh->do(
|
||||
"INSERT INTO user_group_map (user_id, group_id, grant_type, isbless) VALUES ".
|
||||
join(', ', map {
|
||||
my $uid = $_;
|
||||
map { "($uid, $_, ".GRANT_DIRECT.", $isbless)" } keys %{$g->{$uid}};
|
||||
} keys %$g)
|
||||
);
|
||||
# Record profiles_activity entries
|
||||
my $cur_userid = Bugzilla->user->id;
|
||||
my $group_fldid = Bugzilla->get_field('bug_group')->id;
|
||||
if (!$isbless)
|
||||
{
|
||||
# FIXME: should create profiles_activity entries for blesser changes.
|
||||
$dbh->do(
|
||||
"INSERT INTO profiles_activity (userid, who, profiles_when, fieldid, oldvalue, newvalue) VALUES ".
|
||||
join(', ', map {
|
||||
my $uid = $_;
|
||||
join(', ', map { "($uid, $cur_userid, NOW(), $group_fldid, '', ".$dbh->quote($_->{group}->name).")" } values %{$g->{$uid}})
|
||||
} keys %$g)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Remove members or blessers from this group - arguments same as in add_user_groups()
|
||||
sub remove_user_groups
|
||||
{
|
||||
shift if $_[0] eq __PACKAGE__;
|
||||
my ($rows, $isbless) = @_;
|
||||
return if !@$rows;
|
||||
# Filter duplicates
|
||||
$isbless = $isbless ? 1 : 0;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
# Remember group objects
|
||||
my $g = { map { $_->{group}->id => $_->{group} } @$rows };
|
||||
# Filter already deleted members
|
||||
my $del = {};
|
||||
for my $row (@{ $dbh->selectall_arrayref(
|
||||
"SELECT user_id, group_id FROM user_group_map WHERE (user_id, group_id, grant_type, isbless) IN (".
|
||||
join(', ', map {
|
||||
my $uid = int(ref $_->{user} ? $_->{user}->id : $_->{user});
|
||||
my $gid = int($_->{group}->id);
|
||||
"($uid, $gid, ".GRANT_DIRECT.", $isbless)";
|
||||
} @$rows).") FOR UPDATE", undef
|
||||
) || [] })
|
||||
{
|
||||
push @{$del->{$row->[0]}}, $row->[1];
|
||||
}
|
||||
return if !%$del;
|
||||
# Apply update
|
||||
$dbh->do(
|
||||
"DELETE FROM user_group_map WHERE (user_id, group_id, grant_type, isbless) IN (".
|
||||
join(', ', map {
|
||||
my $uid = $_;
|
||||
map { "($uid, $_, ".GRANT_DIRECT.", $isbless)" } @{$del->{$uid}};
|
||||
} keys %$del).")"
|
||||
);
|
||||
# Record profiles_activity entries
|
||||
my $cur_userid = Bugzilla->user->id;
|
||||
my $group_fldid = Bugzilla->get_field('bug_group')->id;
|
||||
if (!$isbless)
|
||||
{
|
||||
# FIXME: should create profiles_activity entries for blesser changes.
|
||||
$dbh->do(
|
||||
"INSERT INTO profiles_activity (userid, who, profiles_when, fieldid, oldvalue, newvalue) VALUES ".
|
||||
join(', ', map {
|
||||
my $uid = $_;
|
||||
map { "($uid, $cur_userid, NOW(), $group_fldid, ".$dbh->quote($g->{$_}->name).", '')" } @{$del->{$uid}};
|
||||
} keys %$del)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Add missing entries in bug_group_map for bugs created while
|
||||
# a mandatory group was disabled and which is now enabled again.
|
||||
sub _enforce_mandatory
|
||||
|
|
|
@ -17,7 +17,7 @@ use Bugzilla::Constants;
|
|||
use Bugzilla::Error;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Util qw(extract_flags filter filter_wants validate translate);
|
||||
use Bugzilla::WebService::Util qw(extract_flags filter filter_wants validate translate user_to_hash);
|
||||
use Bugzilla::Bug;
|
||||
use Bugzilla::BugMail;
|
||||
use Bugzilla::Util qw(trick_taint trim diff_arrays detaint_natural);
|
||||
|
@ -1137,7 +1137,7 @@ sub _bug_to_hash {
|
|||
}
|
||||
if (filter_wants $params, 'assigned_to') {
|
||||
$item{'assigned_to'} = $self->type('email', $bug->assigned_to->login);
|
||||
$item{'assigned_to_detail'} = $self->_user_to_hash($bug->assigned_to, $params, undef, 'assigned_to');
|
||||
$item{'assigned_to_detail'} = user_to_hash($self, $bug->assigned_to, $params);
|
||||
}
|
||||
if (filter_wants $params, 'blocks') {
|
||||
my @blocks = map { $self->type('int', $_) } @{ $bug->blocked };
|
||||
|
@ -1152,14 +1152,14 @@ sub _bug_to_hash {
|
|||
if (filter_wants $params, 'cc') {
|
||||
my @cc = map { $self->type('email', $_) } @{ $bug->cc };
|
||||
$item{'cc'} = \@cc;
|
||||
$item{'cc_detail'} = [ map { $self->_user_to_hash($_, $params, undef, 'cc') } @{ $bug->cc_users } ];
|
||||
$item{'cc_detail'} = [ map { user_to_hash($self, $_, $params) } @{ $bug->cc_users } ];
|
||||
}
|
||||
if (filter_wants $params, 'creation_time') {
|
||||
$item{'creation_time'} = $self->type('dateTime', $bug->creation_ts);
|
||||
}
|
||||
if (filter_wants $params, 'creator') {
|
||||
$item{'creator'} = $self->type('email', $bug->reporter->login);
|
||||
$item{'creator_detail'} = $self->_user_to_hash($bug->reporter, $params, undef, 'creator');
|
||||
$item{'creator_detail'} = user_to_hash($self, $bug->reporter, $params);
|
||||
}
|
||||
if (filter_wants $params, 'depends_on') {
|
||||
my @depends_on = map { $self->type('int', $_) } @{ $bug->dependson };
|
||||
|
@ -1192,7 +1192,7 @@ sub _bug_to_hash {
|
|||
my $qa_login = $bug->qa_contact ? $bug->qa_contact->login : '';
|
||||
$item{'qa_contact'} = $self->type('email', $qa_login);
|
||||
if ($bug->qa_contact) {
|
||||
$item{'qa_contact_detail'} = $self->_user_to_hash($bug->qa_contact, $params, undef, 'qa_contact');
|
||||
$item{'qa_contact_detail'} = user_to_hash($self, $bug->qa_contact, $params);
|
||||
}
|
||||
}
|
||||
if (filter_wants $params, 'see_also') {
|
||||
|
@ -1253,17 +1253,6 @@ sub _bug_to_hash {
|
|||
return \%item;
|
||||
}
|
||||
|
||||
sub _user_to_hash {
|
||||
my ($self, $user, $filters, $types, $prefix) = @_;
|
||||
my $item = filter $filters, {
|
||||
id => $self->type('int', $user->id),
|
||||
real_name => $self->type('string', $user->realname),
|
||||
name => $self->type('email', $user->login),
|
||||
email => $self->type('email', $user->email),
|
||||
}, $types, $prefix;
|
||||
return $item;
|
||||
}
|
||||
|
||||
sub _attachment_to_hash {
|
||||
my ($self, $attach, $filters, $types, $prefix) = @_;
|
||||
|
||||
|
|
|
@ -13,12 +13,16 @@ use warnings;
|
|||
use base qw(Bugzilla::WebService);
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::WebService::Util qw(validate translate params_to_objects);
|
||||
use Bugzilla::WebService::Util qw(validate translate params_to_objects user_to_hash);
|
||||
|
||||
use constant PUBLIC_METHODS => qw(
|
||||
create
|
||||
get
|
||||
update
|
||||
add_members
|
||||
remove_members
|
||||
add_managers
|
||||
remove_managers
|
||||
);
|
||||
|
||||
use constant MAPPED_RETURNS => {
|
||||
|
@ -146,6 +150,92 @@ sub get {
|
|||
return { groups => \@groups };
|
||||
}
|
||||
|
||||
sub add_members
|
||||
{
|
||||
my $self = shift;
|
||||
$self->_update_users(0, 1, @_);
|
||||
}
|
||||
|
||||
sub remove_members
|
||||
{
|
||||
my $self = shift;
|
||||
$self->_update_users(0, 0, @_);
|
||||
}
|
||||
|
||||
sub add_managers
|
||||
{
|
||||
my $self = shift;
|
||||
$self->_update_users(1, 1, @_);
|
||||
}
|
||||
|
||||
sub remove_managers
|
||||
{
|
||||
my $self = shift;
|
||||
$self->_update_users(1, 0, @_);
|
||||
}
|
||||
|
||||
sub _update_users
|
||||
{
|
||||
my ($self, $isbless, $add, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
defined($params->{names}) || defined($params->{ids})
|
||||
|| ThrowCodeError('params_required',
|
||||
{ function => 'Group.update', params => ['ids', 'names'] });
|
||||
my @group_objects;
|
||||
if ($params->{usernames})
|
||||
{
|
||||
push @group_objects, @{ Bugzilla::Group->match({ name => $params->{names} }) };
|
||||
}
|
||||
if ($params->{userids})
|
||||
{
|
||||
push @group_objects, @{ Bugzilla::Group->match({ id => $params->{ids} }) };
|
||||
}
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
if ($isbless)
|
||||
{
|
||||
Bugzilla->user->in_group('editusers')
|
||||
|| ThrowUserError("auth_failure", {
|
||||
group => "editusers",
|
||||
action => "edit",
|
||||
object => "group"
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Bugzilla->user->in_group('creategroups')
|
||||
|| !grep { !Bugzilla->user->can_bless($_->id) } @group_objects
|
||||
|| ThrowUserError("auth_failure", {
|
||||
group => "creategroups",
|
||||
action => "edit",
|
||||
object => "group"
|
||||
});
|
||||
}
|
||||
|
||||
my @user_objects;
|
||||
if ($params->{usernames})
|
||||
{
|
||||
push @user_objects, @{ Bugzilla::User->match({ login_name => $params->{usernames} }) };
|
||||
}
|
||||
if ($params->{userids})
|
||||
{
|
||||
push @user_objects, @{ Bugzilla::User->match({ id => $params->{userids} }) };
|
||||
}
|
||||
@user_objects = grep { Bugzilla->user->can_see_user($_) } @user_objects;
|
||||
@user_objects || ThrowCodeError('params_required', { function => 'Group.update', params => ['userids', 'usernames'] });
|
||||
|
||||
$add = $add ? 'add_user_groups' : 'remove_user_groups';
|
||||
Bugzilla::Group->$add(
|
||||
[ map { my $grp = $_; map { { group => $grp, user => $_ } } @user_objects } @group_objects ], $isbless
|
||||
);
|
||||
|
||||
return {
|
||||
groups => [ map { $self->_group_to_hash({}, $_) } @group_objects ],
|
||||
users => [ map { user_to_hash($self, $_, {}) } @user_objects ],
|
||||
};
|
||||
}
|
||||
|
||||
sub _group_to_hash {
|
||||
my ($self, $params, $group) = @_;
|
||||
my $user = Bugzilla->user;
|
||||
|
|
|
@ -17,7 +17,7 @@ use Bugzilla::Error;
|
|||
use Bugzilla::Group;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util qw(trim detaint_natural);
|
||||
use Bugzilla::WebService::Util qw(filter filter_wants validate translate params_to_objects);
|
||||
use Bugzilla::WebService::Util qw(filter filter_wants validate translate params_to_objects user_to_hash);
|
||||
|
||||
use List::Util qw(first min);
|
||||
|
||||
|
@ -240,13 +240,7 @@ sub get {
|
|||
|
||||
my $in_group = $self->_filter_users_by_group(\@user_objects, $params);
|
||||
foreach my $user (@$in_group) {
|
||||
my $user_info = filter $params, {
|
||||
id => $self->type('int', $user->id),
|
||||
real_name => $self->type('string', $user->realname),
|
||||
name => $self->type('email', $user->login),
|
||||
email => $self->type('email', $user->email),
|
||||
can_login => $self->type('boolean', $user->is_enabled ? 1 : 0),
|
||||
};
|
||||
my $user_info = user_to_hash($self, $user);
|
||||
|
||||
if (Bugzilla->user->in_group('editusers')) {
|
||||
$user_info->{email_enabled} = $self->type('boolean', $user->email_enabled);
|
||||
|
|
|
@ -31,6 +31,7 @@ our @EXPORT_OK = qw(
|
|||
validate
|
||||
translate
|
||||
params_to_objects
|
||||
user_to_hash
|
||||
fix_credentials
|
||||
);
|
||||
|
||||
|
@ -260,6 +261,17 @@ sub params_to_objects {
|
|||
return \@objects;
|
||||
}
|
||||
|
||||
sub user_to_hash {
|
||||
my ($ws, $user, $params) = @_;
|
||||
return filter $params, {
|
||||
id => $ws->type('int', $user->id),
|
||||
real_name => $ws->type('string', $user->realname),
|
||||
name => $ws->type('email', $user->login),
|
||||
email => $ws->type('email', $user->email),
|
||||
can_login => $ws->type('boolean', $user->is_enabled ? 1 : 0),
|
||||
};
|
||||
}
|
||||
|
||||
sub fix_credentials {
|
||||
my ($params) = @_;
|
||||
# Allow user to pass in login=foo&password=bar as a convenience
|
||||
|
|
|
@ -302,6 +302,7 @@ if ($action eq 'confirm_remove')
|
|||
if ($action eq 'remove_regexp')
|
||||
{
|
||||
check_token_data($token, 'remove_group_members');
|
||||
|
||||
# remove all explicit users from the group with
|
||||
# gid = $ARGS->{group} that match the regular expression
|
||||
# stored in the DB for that group or all of them period
|
||||
|
@ -310,25 +311,14 @@ if ($action eq 'remove_regexp')
|
|||
my $regexp = CheckGroupRegexp($ARGS->{regexp});
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
my $users = $group->members_direct();
|
||||
my $sth_delete = $dbh->prepare("DELETE FROM user_group_map WHERE user_id = ? AND isbless = 0 AND group_id = ?");
|
||||
|
||||
my @deleted;
|
||||
foreach my $member (@$users)
|
||||
{
|
||||
if ($regexp eq '' || $member->login =~ m/$regexp/i)
|
||||
{
|
||||
$sth_delete->execute($member->id, $group->id);
|
||||
push @deleted, $member;
|
||||
}
|
||||
}
|
||||
my $del = [ grep { $_->login =~ m/$regexp/is } @{ $group->members_direct } ];
|
||||
$group->remove_users($del, 0);
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
$vars->{users} = \@deleted;
|
||||
$vars->{users} = $del;
|
||||
$vars->{regexp} = $regexp;
|
||||
|
||||
Bugzilla::Hook::process('editgroups-post_remove_regexp', { deleted => \@deleted });
|
||||
Bugzilla::Hook::process('editgroups-post_remove_regexp', { deleted => $del });
|
||||
Bugzilla::Views::refresh_some_views();
|
||||
|
||||
delete_token($token);
|
||||
|
|
|
@ -279,22 +279,12 @@ elsif ($action eq 'update')
|
|||
$changes = $otherUser->update();
|
||||
}
|
||||
|
||||
# Update group settings.
|
||||
my $sth_add_mapping = $dbh->prepare(
|
||||
"INSERT INTO user_group_map (user_id, group_id, isbless, grant_type) VALUES (?, ?, ?, ?)"
|
||||
);
|
||||
my $sth_remove_mapping = $dbh->prepare(
|
||||
"DELETE FROM user_group_map WHERE user_id=? AND group_id=? AND isbless=? AND grant_type=?"
|
||||
);
|
||||
|
||||
my @groupsAddedTo;
|
||||
my @groupsRemovedFrom;
|
||||
my @groupsGrantedRightsToBless;
|
||||
my @groupsDeniedRightsToBless;
|
||||
|
||||
# Regard only groups the user is allowed to bless and skip all others silently.
|
||||
# FIXME: checking for existence of each user_group_map entry would allow to
|
||||
# display a friendlier error message on page reloads.
|
||||
userDataToVars($otherUserID);
|
||||
my $permissions = $vars->{permissions};
|
||||
foreach my $blessable (@{$user->bless_groups()})
|
||||
|
@ -308,13 +298,11 @@ elsif ($action eq 'update')
|
|||
{
|
||||
if (!$groupid)
|
||||
{
|
||||
$sth_remove_mapping->execute($otherUserID, $id, 0, GRANT_DIRECT);
|
||||
push @groupsRemovedFrom, $name;
|
||||
push @groupsRemovedFrom, $blessable;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sth_add_mapping->execute($otherUserID, $id, 0, GRANT_DIRECT);
|
||||
push @groupsAddedTo, $name;
|
||||
push @groupsAddedTo, $blessable;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,27 +315,20 @@ elsif ($action eq 'update')
|
|||
{
|
||||
if (!$groupid)
|
||||
{
|
||||
$sth_remove_mapping->execute($otherUserID, $id, 1, GRANT_DIRECT);
|
||||
push @groupsDeniedRightsToBless, $name;
|
||||
push @groupsDeniedRightsToBless, $blessable;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sth_add_mapping->execute($otherUserID, $id, 1, GRANT_DIRECT);
|
||||
push @groupsGrantedRightsToBless, $name;
|
||||
push @groupsGrantedRightsToBless, $blessable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (@groupsAddedTo || @groupsRemovedFrom)
|
||||
{
|
||||
$dbh->do(
|
||||
"INSERT INTO profiles_activity (userid, who, profiles_when, fieldid, oldvalue, newvalue)".
|
||||
" VALUES (?, ?, NOW(), ?, ?, ?)", undef,
|
||||
$otherUserID, $userid, Bugzilla->get_field('bug_group')->id,
|
||||
join(', ', @groupsRemovedFrom), join(', ', @groupsAddedTo)
|
||||
);
|
||||
}
|
||||
# FIXME: should create profiles_activity entries for blesser changes.
|
||||
|
||||
Bugzilla::Group::add_user_groups([ map { { group => $_, user => $otherUser } } @groupsAddedTo ]);
|
||||
Bugzilla::Group::remove_user_groups([ map { { group => $_, user => $otherUser } } @groupsRemovedFrom ]);
|
||||
Bugzilla::Group::add_user_groups([ map { { group => $_, user => $otherUser } } @groupsGrantedRightsToBless ], 1);
|
||||
Bugzilla::Group::remove_user_groups([ map { { group => $_, user => $otherUser } } @groupsDeniedRightsToBless ], 1);
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
|
|
|
@ -54,30 +54,14 @@ if (@add_members || @add_bless || @rm_members || @rm_bless)
|
|||
} };
|
||||
for (\@add_members, \@add_bless)
|
||||
{
|
||||
@$_ = map { $users->{lc $_} ? $users->{lc $_}->id : ThrowUserError('invalid_username', { name => $_ }) } @$_;
|
||||
}
|
||||
if (@add_members || @add_bless)
|
||||
{
|
||||
# FIXME Use object method instead of direct DB query
|
||||
Bugzilla->dbh->do(
|
||||
"INSERT IGNORE INTO user_group_map (user_id, group_id, grant_type, isbless) VALUES ".
|
||||
join(', ', ("(?, ?, ?, ?)") x (@add_members + @add_bless)), undef,
|
||||
(map { $_, $vars->{group}->id, GRANT_DIRECT, 0 } @add_members),
|
||||
(map { $_, $vars->{group}->id, GRANT_DIRECT, 1 } @add_bless)
|
||||
);
|
||||
@$_ = map { $users->{lc $_} || ThrowUserError('invalid_username', { name => $_ }) } @$_;
|
||||
}
|
||||
$vars->{group}->add_users(\@add_members, 0);
|
||||
$vars->{group}->add_users(\@add_bless, 1);
|
||||
}
|
||||
if (@rm_members || @rm_bless)
|
||||
{
|
||||
# FIXME Use object method instead of direct DB query
|
||||
trick_taint($_) for @rm_members, @rm_bless;
|
||||
Bugzilla->dbh->do(
|
||||
"DELETE FROM user_group_map WHERE group_id=? AND grant_type=? AND (user_id, isbless) IN (".
|
||||
join(', ', ("(?, ?)") x (@rm_members + @rm_bless)).")", undef,
|
||||
$vars->{group}->id, GRANT_DIRECT,
|
||||
(map { int($_), 0 } @rm_members), (map { int($_), 1 } @rm_bless)
|
||||
);
|
||||
}
|
||||
trick_taint($_) for @rm_members, @rm_bless;
|
||||
$vars->{group}->remove_users(\@rm_members, 0);
|
||||
$vars->{group}->remove_users(\@rm_bless, 1);
|
||||
if (@add_members || @rm_members)
|
||||
{
|
||||
Bugzilla::Hook::process('editusersingroup-post_add', {
|
||||
|
|
Loading…
Reference in New Issue