Commit 2a5d8de8 authored by travis%sedsystems.ca's avatar travis%sedsystems.ca

Bug 119485 : Templatise editusers.cgi

Patch by Marc Schumann <wurblzap@gmail.com> r=GavinS, mkanat a=justdave
parent 86cdd89c
......@@ -86,6 +86,7 @@ sub _create {
'name' => '',
'login' => '',
'showmybugslink' => 0,
'disabledtext' => '',
'flags' => {},
};
bless ($self, $class);
......@@ -101,9 +102,11 @@ sub _create {
my ($id,
$login,
$name,
$disabledtext,
$mybugslink) = $dbh->selectrow_array(qq{SELECT userid,
login_name,
realname,
disabledtext,
mybugslink
FROM profiles
WHERE $cond},
......@@ -115,6 +118,7 @@ sub _create {
$self->{'id'} = $id;
$self->{'name'} = $name;
$self->{'login'} = $login;
$self->{'disabledtext'} = $disabledtext;
$self->{'showmybugslink'} = $mybugslink;
# Now update any old group information if needed
......@@ -951,12 +955,14 @@ sub get_userlist {
return $self->{'userlist'};
}
sub insert_new_user ($$) {
my ($username, $realname) = (@_);
sub insert_new_user ($$;$$) {
my ($username, $realname, $password, $disabledtext) = (@_);
my $dbh = Bugzilla->dbh;
# Generate a new random password for the user.
my $password = &::GenerateRandomPassword();
$disabledtext ||= '';
# If not specified, generate a new random password for the user.
$password ||= &::GenerateRandomPassword();
my $cryptpassword = bz_crypt($password);
# XXX - These should be moved into ValidateNewUser or CheckEmailSyntax
......@@ -966,10 +972,12 @@ sub insert_new_user ($$) {
# Insert the new user record into the database.
$dbh->do("INSERT INTO profiles
(login_name, realname, cryptpassword, emailflags)
VALUES (?, ?, ?, ?)",
(login_name, realname, cryptpassword, emailflags,
disabledtext)
VALUES (?, ?, ?, ?, ?)",
undef,
($username, $realname, $cryptpassword, DEFAULT_EMAIL_SETTINGS));
($username, $realname, $cryptpassword, DEFAULT_EMAIL_SETTINGS,
$disabledtext));
# Return the password to the calling code so it can be included
# in an email sent to the user.
......@@ -1039,7 +1047,7 @@ Bugzilla::User - Object for a Bugzilla user
my $user = new Bugzilla::User($id);
# Class Functions
$random_password = insert_new_user($username, $realname);
$password = insert_new_user($username, $realname, $password, $disabledtext);
=head1 DESCRIPTION
......@@ -1132,6 +1140,10 @@ linkinfooter - Whether or not the query should be displayed in the footer.
=back
=item C<disabledtext>
Returns the disable text of the user, if any.
=item C<flush_queries_cache>
Some code modifies the set of stored queries. Because C<Bugzilla::User> does
......@@ -1254,12 +1266,18 @@ called "statically," just like a normal procedural function.
=item C<insert_new_user>
Creates a new user in the database with a random password.
Creates a new user in the database.
Params: $username (scalar, string) - The login name for the new user.
$realname (scalar, string) - The full name for the new user.
Returns: The password that we randomly generated for this user, in plain text.
$password (scalar, string) - Optional. The password for the new user;
if not given, a random password will be
generated.
$disabledtext (scalar, string) - Optional. The disable text for the new
user; if not given, it will be empty.
Returns: The password for this user, in plain text, so it can be included
in an e-mail sent to the user.
=item C<is_available_username>
......
......@@ -213,7 +213,7 @@ sub CheckEmailSyntax {
my ($addr) = (@_);
my $match = Param('emailregexp');
if ($addr !~ /$match/ || $addr =~ /[\\\(\)<>&,;:"\[\] \t\r\n]/) {
ThrowUserError("illegal_email_address", { addr => $addr });
ThrowUserError("illegal_email_address", { addr => $addr }, 'abort');
}
}
......
......@@ -1061,10 +1061,12 @@ Reason: %reason%
{
name => 'allowuserdeletion',
desc => 'The pages to edit users can also let you delete a user. But there ' .
'is no code that goes and cleans up any references to that user in ' .
'other tables, so such deletions are kinda scary. So, you have to ' .
'turn on this option before any such deletions will ever happen.',
desc => 'The pages to edit users can also let you delete a user. ' .
'Bugzilla will issue a warning in case you\'d run into ' .
'inconsistencies when you\'re about to do so, ' .
'but such deletions remain kinda scary. ' .
'So, you have to turn on this option before any such deletions ' .
'will ever happen.',
type => 'b',
default => 0
},
......
......@@ -11,23 +11,9 @@
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is mozilla.org code.
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Holger
# Schurig. Portions created by Holger Schurig are
# Copyright (C) 1999 Holger Schurig. All
# Rights Reserved.
#
# Contributor(s): Holger Schurig <holgerschurig@nikocity.de>
# Dave Miller <justdave@syndicomm.com>
# Joe Robins <jmrobins@tgix.com>
# Dan Mosedale <dmose@mozilla.org>
# Joel Peshkin <bugreport@peshkin.net>
# Erik Stambaugh <erik@dasbistro.com>
#
# Direct any questions on this source code to
#
# Holger Schurig <holgerschurig@nikocity.de>
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
use strict;
use lib ".";
......@@ -35,827 +21,735 @@ use lib ".";
require "CGI.pl";
require "globals.pl";
use Bugzilla;
use vars qw( $vars );
use Bugzilla::User;
use Bugzilla::Constants;
use Bugzilla::Auth;
# Shut up misguided -w warnings about "used only once". "use vars" just
# doesn't work for me.
sub sillyness {
my $zz;
$zz = $::userid;
}
my $editall;
# TestUser: just returns if the specified user does exists
# CheckUser: same check, optionally emit an error text
sub TestUser ($)
{
my $user = shift;
# does the product exist?
SendSQL("SELECT login_name
FROM profiles
WHERE login_name=" . SqlQuote($user));
return FetchOneColumn();
}
sub CheckUser ($)
{
my $user = shift;
# do we have a user?
unless ($user) {
print "Sorry, you haven't specified a user.";
PutTrailer();
exit;
}
unless (TestUser $user) {
print "Sorry, user '$user' does not exist.";
PutTrailer();
exit;
}
}
Bugzilla->login(LOGIN_REQUIRED);
my $cgi = Bugzilla->cgi();
my $template = Bugzilla->template();
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user();
my $userid = $user->id();
my $editusers = UserInGroup('editusers');
my $action = $cgi->param('action') || 'search';
# Reject access if there is no sense in continuing.
$editusers
|| Bugzilla->user->can_bless()
|| ThrowUserError("auth_failure", {group => "editusers",
reason => "cant_bless",
action => "edit",
object => "users"});
print Bugzilla->cgi->header();
sub EmitElement ($$)
{
my ($name, $value) = (@_);
$value = value_quote($value);
if ($editall) {
print qq{<TD><INPUT SIZE=64 MAXLENGTH=255 NAME="$name" VALUE="$value"></TD>\n};
$vars->{'editusers'} = $editusers;
mirrorListSelectionValues();
###########################################################################
if ($action eq 'search') {
# Allow to restrict the search to any group the user is allowed to bless.
$vars->{'restrictablegroups'} = groupsUserMayBless($user, 'id', 'name');
$template->process('admin/users/search.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'list') {
my $matchstr = $cgi->param('matchstr');
my $matchtype = $cgi->param('matchtype');
my $grouprestrict = $cgi->param('grouprestrict') || '0';
my $groupid = $cgi->param('groupid');
my $query = 'SELECT DISTINCT userid, login_name, realname, disabledtext ' .
'FROM profiles';
my @bindValues;
my $nextCondition;
if (Param('usevisibilitygroups')) {
# Show only users in visible groups.
my $visibleGroups = visibleGroupsAsString();
$query .= qq{, user_group_map AS ugm
WHERE ugm.user_id = profiles.userid
AND ugm.isbless = 0
AND ugm.group_id IN ($visibleGroups)
};
$nextCondition = 'AND';
} else {
print qq{<TD>$value<INPUT TYPE=HIDDEN NAME="$name" VALUE="$value"></TD>\n};
if ($grouprestrict eq '1') {
$query .= ', user_group_map AS ugm';
}
$nextCondition = 'WHERE';
}
}
#
# Displays the form to edit a user parameters
#
sub EmitFormElements ($$$$)
{
my ($user_id, $user, $realname, $disabledtext) = @_;
print " <TH ALIGN=\"right\">Login name:</TH>\n";
EmitElement("user", $user);
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Real name:</TH>\n";
EmitElement("realname", $realname);
if ($editall) {
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Password:</TH>\n";
print qq|
<TD><INPUT TYPE="PASSWORD" SIZE="16" MAXLENGTH="16" NAME="password" VALUE=""><br>
(enter new password to change)
</TD>
|;
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Disable text:</TH>\n";
print " <TD ROWSPAN=2><TEXTAREA NAME=\"disabledtext\" ROWS=10 COLS=60>" .
value_quote($disabledtext) . "</TEXTAREA>\n";
print " </TD>\n";
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">If non-empty, then the account will\n";
print "be disabled, and this text should explain why.</TD>\n";
}
if($user ne "") {
print "</TR><TR><TH VALIGN=TOP ALIGN=RIGHT>Group Access:</TH><TD><TABLE><TR>";
SendSQL("SELECT groups.id, groups.name, groups.description, " .
"MAX(CASE WHEN grant_type = " . GRANT_DIRECT . " THEN 1 ELSE 0 END)," .
"MAX(CASE WHEN grant_type = " . GRANT_DERIVED . " THEN 1 ELSE 0 END)," .
"MAX(CASE WHEN grant_type = " . GRANT_REGEXP . " THEN 1 ELSE 0 END)" .
"FROM groups " .
"LEFT JOIN user_group_map " .
"ON user_group_map.group_id = groups.id " .
"AND isbless = 0 " .
"AND user_id = $user_id " .
"GROUP BY groups.name ");
if (MoreSQLData()) {
if ($editall) {
print "<TD COLSPAN=3 ALIGN=LEFT><B>Can turn this bit on for other users</B></TD>\n";
print "</TR><TR>\n<TD ALIGN=CENTER><B>|</B></TD>\n";
}
print "<TD COLSPAN=2 ALIGN=LEFT><B>User is a member of these groups</B></TD>\n";
while (MoreSQLData()) {
my ($groupid, $name, $description, $checked, $isderived, $isregexp) = FetchSQLData();
next unless ($editall || Bugzilla->user->can_bless($name));
PushGlobalSQLState();
SendSQL("SELECT user_id " .
"FROM user_group_map " .
"WHERE isbless = 1 " .
"AND user_id = $user_id " .
"AND group_id = $groupid");
my ($blchecked) = FetchSQLData() ? 1 : 0;
SendSQL("SELECT grantor_id FROM user_group_map,
group_group_map
WHERE $groupid = grantor_id
AND user_group_map.user_id = $user_id
AND user_group_map.isbless = 0
AND group_group_map.grant_type = " . GROUP_BLESS . "
AND user_group_map.group_id = member_id");
my $derivedbless = FetchOneColumn();
PopGlobalSQLState();
print "</TR><TR";
print ' bgcolor=#cccccc' if ($isderived || $isregexp);
print ">\n";
print "<INPUT TYPE=HIDDEN NAME=\"oldgroup_$groupid\" VALUE=\"$checked\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"oldbless_$groupid\" VALUE=\"$blchecked\">\n";
if ($editall) {
$blchecked = ($blchecked) ? "CHECKED" : "";
print "<TD ALIGN=CENTER>";
print "[" if $derivedbless;
print "<INPUT TYPE=CHECKBOX NAME=\"bless_$groupid\" $blchecked VALUE=\"$groupid\">";
print "]" if $derivedbless;
print "</TD>\n";
}
$checked = ($checked) ? "CHECKED" : "";
print "<TD ALIGN=CENTER>";
print '[' if ($isderived);
print '*' if ($isregexp);
print "<INPUT TYPE=CHECKBOX NAME=\"group_$groupid\" $checked VALUE=\"$groupid\">";
print ']' if ($isderived);
print '*' if ($isregexp);
print "</TD><TD><B>";
print ucfirst($name) . "</B>: $description</TD>\n";
}
# Selection by user name.
if (defined($matchtype)) {
$query .= " $nextCondition profiles.login_name ";
if ($matchtype eq 'regexp') {
$query .= $dbh->sql_regexp . ' ?';
$matchstr = '.' unless $matchstr;
} elsif ($matchtype eq 'notregexp') {
$query .= $dbh->sql_not_regexp . ' ?';
$matchstr = '.' unless $matchstr;
} else { # substr or unknown
$query .= 'like ?';
$matchstr = "%$matchstr%";
}
print "</TR></TABLE></TD>\n";
$nextCondition = 'AND';
# We can trick_taint because we use the value in a SELECT only, using
# a placeholder.
trick_taint($matchstr);
push(@bindValues, $matchstr);
}
}
#
# Displays a text like "a.", "a or b.", "a, b or c.", "a, b, c or d."
#
sub PutTrailer (@)
{
my (@links) = ("Back to the <a href=\"./\">index</a>");
if($editall) {
push(@links,
"<a href=\"editusers.cgi?action=add\">add</a> a new user");
# Selection by group.
if ($grouprestrict eq '1') {
$query .= " $nextCondition profiles.userid = ugm.user_id " .
'AND ugm.group_id = ?';
# We can trick_taint because we use the value in a SELECT only, using
# a placeholder.
trick_taint($groupid);
push(@bindValues, $groupid);
}
push(@links, @_);
my $count = $#links;
my $num = 0;
print "<P>\n";
foreach (@links) {
print $_;
if ($num == $count) {
print ".\n";
$query .= ' ORDER BY profiles.login_name';
$vars->{'users'} = $dbh->selectall_arrayref($query,
{'Slice' => {}},
@bindValues);
$template->process('admin/users/list.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'add') {
$editusers || ThrowUserError("auth_failure", {group => "editusers",
action => "add",
object => "users"});
$template->process('admin/users/create.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'new') {
$editusers || ThrowUserError("auth_failure", {group => "editusers",
action => "add",
object => "users"});
my $login = $cgi->param('login');
my $password = $cgi->param('password');
# Cleanups
my $realname = trim($cgi->param('name') || '');
my $disabledtext = trim($cgi->param('disabledtext') || '');
# Lock tables during the check+creation session.
$dbh->bz_lock_tables('profiles WRITE',
'profiles_activity WRITE',
'namedqueries READ',
'whine_queries READ',
'tokens READ');
# Validity checks
$login || ThrowUserError('user_login_required');
CheckEmailSyntax($login);
is_available_username($login) || ThrowUserError('account_exists',
{'email' => $login});
ValidatePassword($password);
# Login and password are validated now, and realname and disabledtext
# are allowed to contain anything
trick_taint($login);
trick_taint($realname);
trick_taint($password);
trick_taint($disabledtext);
insert_new_user($login, $realname, $password, $disabledtext);
my $userid = $dbh->bz_last_key('profiles', 'userid');
$dbh->bz_unlock_tables();
userDataToVars($userid);
$vars->{'message'} = 'account_created';
$template->process('admin/users/edit.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'edit') {
my $otherUser = new Bugzilla::User($cgi->param('userid'))
|| ThrowCodeError('invalid_user_id', {'userid' => $cgi->param('userid')});
my $otherUserID = $otherUser->id();
canSeeUser($otherUserID)
|| ThrowUserError('auth_failure', {reason => "not_visible",
action => "modify",
object => "user"});
userDataToVars($otherUserID);
$template->process('admin/users/edit.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'update') {
my $otherUser = new Bugzilla::User($cgi->param('userid'))
|| ThrowCodeError('invalid_user_id', {'userid' => $cgi->param('userid')});
my $otherUserID = $otherUser->id();
my $logoutNeeded = 0;
my @changedFields;
# Lock tables during the check+update session.
$dbh->bz_lock_tables('profiles WRITE',
'profiles_activity WRITE',
'fielddefs READ',
'namedqueries READ',
'whine_queries READ',
'tokens WRITE',
'logincookies WRITE',
'groups READ',
'user_group_map WRITE',
'group_group_map READ');
canSeeUser($otherUserID)
|| ThrowUserError('auth_failure', {reason => "not_visible",
action => "modify",
object => "user"},
'abort');
# Cleanups
my $login = trim($cgi->param('login') || '');
my $loginold = $cgi->param('loginold') || '';
my $realname = trim($cgi->param('name') || '');
my $realnameold = $cgi->param('nameold') || '';
my $password = $cgi->param('password') || '';
my $disabledtext = trim($cgi->param('disabledtext') || '');
my $disabledtextold = $cgi->param('disabledtextold') || '';
# Update profiles table entry; silently skip doing this if the user
# is not authorized.
if ($editusers) {
my @values;
if ($login ne $loginold) {
# Validate, then trick_taint.
$login || ThrowUserError('user_login_required', undef, 'abort');
CheckEmailSyntax($login);
is_available_username($login) || ThrowUserError('account_exists',
{'email' => $login},
'abort');
trick_taint($login);
push(@changedFields, 'login_name');
push(@values, $login);
$logoutNeeded = 1;
# Since we change the login, silently delete any tokens.
$dbh->do('DELETE FROM tokens WHERE userid = ?', {}, $otherUserID);
}
elsif ($num == $count-1) {
print " or ";
if ($realname ne $realnameold) {
# The real name may be anything; we use a placeholder for our
# INSERT, and we rely on displaying code to FILTER html.
trick_taint($realname);
push(@changedFields, 'realname');
push(@values, $realname);
}
else {
print ", ";
if ($password) {
# Validate, then trick_taint.
ValidatePassword($password) if $password;
trick_taint($password);
push(@changedFields, 'cryptpassword');
push(@values, bz_crypt($password));
$logoutNeeded = 1;
}
$num++;
}
PutFooter();
}
#
# Preliminary checks:
#
Bugzilla->login(LOGIN_REQUIRED);
print Bugzilla->cgi->header();
$editall = UserInGroup("editusers");
$editall
|| Bugzilla->user->can_bless
|| ThrowUserError("auth_failure", {group => "editusers",
reason => "cant_bless",
action => "edit",
object => "users"});
#
# often used variables
#
my $user = trim($::FORM{user} || '');
my $action = trim($::FORM{action} || '');
my $localtrailer = '<a href="editusers.cgi?">edit more users</a>';
my $candelete = Param('allowuserdeletion');
my $dbh = Bugzilla->dbh;
#
# action='' -> Ask for match string for users.
#
unless ($action) {
PutHeader("Select match string");
print qq{
<FORM METHOD=GET ACTION="editusers.cgi">
<INPUT TYPE=HIDDEN NAME="action" VALUE="list">
List users with login name matching:
<INPUT SIZE=32 NAME="matchstr">
<SELECT NAME="matchtype">
<OPTION VALUE="substr" SELECTED>case-insensitive substring
<OPTION VALUE="regexp">case-sensitive regexp
<OPTION VALUE="notregexp">not (case-sensitive regexp)
</SELECT>
<BR>
<INPUT TYPE=SUBMIT VALUE="Submit">
</FORM>
};
PutTrailer();
exit;
}
#
# action='list' -> Show nice list of matching users
#
if ($action eq 'list') {
PutHeader("Select user");
my $query = "";
my $matchstr = $::FORM{'matchstr'};
if (exists $::FORM{'matchtype'}) {
$query = "SELECT login_name,realname,disabledtext " .
"FROM profiles WHERE login_name ";
if ($::FORM{'matchtype'} eq 'substr') {
$query .= "like";
$matchstr = '%' . $matchstr . '%';
} elsif ($::FORM{'matchtype'} eq 'regexp') {
$query .= $dbh->sql_regexp();
$matchstr = '.'
unless $matchstr;
} elsif ($::FORM{'matchtype'} eq 'notregexp') {
$query .= $dbh->sql_not_regexp();
$matchstr = '.'
unless $matchstr;
} else {
die "Unknown match type";
}
$query .= SqlQuote($matchstr) . " ORDER BY login_name";
} elsif (exists $::FORM{'group'}) {
detaint_natural($::FORM{'group'});
$query = "SELECT DISTINCT login_name,realname,disabledtext " .
"FROM profiles, user_group_map WHERE profiles.userid = user_group_map.user_id
AND group_id=" . $::FORM{'group'} . " ORDER BY login_name";
} else {
die "Missing parameters";
}
SendSQL($query);
my $count = 0;
my $header = "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR BGCOLOR=\"#6666FF\">
<TH ALIGN=\"left\">Edit user ...</TH>
<TH ALIGN=\"left\">Real name</TH>
";
if ($candelete) {
$header .= "<TH ALIGN=\"left\">Action</TH>\n";
}
$header .= "</TR>\n";
print $header;
while ( MoreSQLData() ) {
$count++;
if ($count % 100 == 0) {
print "</table>$header";
}
my ($user, $realname, $disabledtext) = FetchSQLData();
my $s = "";
my $e = "";
if ($disabledtext) {
$s = '<span class="bz_inactive">';
$e = '</span>';
if ($disabledtext ne $disabledtextold) {
# The disable text may be anything; we use a placeholder for our
# INSERT, and we rely on displaying code to FILTER html.
trick_taint($disabledtext);
push(@changedFields, 'disabledtext');
push(@values, $disabledtext);
$logoutNeeded = 1;
}
$realname = ($realname ? html_quote($realname) : "<FONT COLOR=\"red\">missing</FONT>");
print "<TR>\n";
print " <TD VALIGN=\"top\"><A HREF=\"editusers.cgi?action=edit&user=", url_quote($user), "\"><B>$s", html_quote($user), "$e</B></A></TD>\n";
print " <TD VALIGN=\"top\">$s$realname$e</TD>\n";
if ($candelete) {
print " <TD VALIGN=\"top\"><A HREF=\"editusers.cgi?action=del&user=", url_quote($user), "\">Delete</A></TD>\n";
if (@changedFields) {
push (@values, $otherUserID);
$logoutNeeded && Bugzilla->logout_user_by_id($otherUserID);
$dbh->do('UPDATE profiles SET ' .
join(' = ?,', @changedFields).' = ? ' .
'WHERE userid = ?',
undef, @values);
# FIXME: should create profiles_activity entries.
}
print "</TR>";
}
if ($editall) {
print "<TR>\n";
my $span = $candelete ? 3 : 2;
print qq{
<TD VALIGN="top" COLSPAN=$span ALIGN="right">
<A HREF=\"editusers.cgi?action=add\">add a new user</A>
</TD>
};
print "</TR>";
}
print "</TABLE>\n";
print "$count users found.\n";
PutTrailer($localtrailer);
exit;
}
#
# action='add' -> present form for parameters for new user
#
# (next action will be 'new')
#
if ($action eq 'add') {
$editall || ThrowUserError("auth_failure", {group => "editusers",
action => "add",
object => "users"});
PutHeader("Add user");
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements(0, '', '', '');
print "</TR></TABLE>\n<HR>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Add\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"new\">\n";
print "</FORM>";
my $other = $localtrailer;
$other =~ s/more/other/;
PutTrailer($other);
exit;
}
# Update group settings.
my $sth_add_mapping = $dbh->prepare(
qq{INSERT INTO user_group_map (
user_id, group_id, isbless, grant_type
) VALUES (
?, ?, ?, ?
)
});
my $sth_remove_mapping = $dbh->prepare(
qq{DELETE FROM user_group_map
WHERE user_id = ?
AND group_id = ?
AND isbless = ?
AND grant_type = ?
});
# We need the group names, too -- for display and for profiles_activity.
my $groups = $dbh->selectall_hashref('SELECT id, name FROM groups', 'id');
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.
foreach (@{groupsUserMayBless($user, 'id')}) {
my $id = $$_{'id'};
# Change memberships.
my $oldgroupid = $cgi->param("oldgroup_$id") || '0';
my $groupid = $cgi->param("group_$id") || '0';
if ($groupid ne $oldgroupid) {
if ($groupid eq '0') {
$sth_remove_mapping->execute(
$otherUserID, $id, 0, GRANT_DIRECT);
push(@groupsRemovedFrom, $$groups{$id}{'name'});
} else {
$sth_add_mapping->execute(
$otherUserID, $id, 0, GRANT_DIRECT);
push(@groupsAddedTo, $$groups{$id}{'name'});
}
}
#
# action='new' -> add user entered in the 'action=add' screen
#
if ($action eq 'new') {
$editall || ThrowUserError("auth_failure", {group => "editusers",
action => "add",
object => "users"});
# Cleanups and valididy checks
my $realname = trim($::FORM{realname} || '');
# We don't trim the password since that could falsely lead the user
# to believe a password with a space was accepted even though a space
# is an illegal character in a Bugzilla password.
my $password = $::FORM{'password'};
my $disabledtext = trim($::FORM{disabledtext} || '');
my $emailregexp = Param("emailregexp");
PutHeader("Adding new user");
unless ($user) {
print "You must enter a name for the new user. Please press\n";
print "<b>Back</b> and try again.\n";
PutTrailer($localtrailer);
exit;
}
unless ($user =~ m/$emailregexp/) {
print "The user name entered must be a valid e-mail address. Please press\n";
print "<b>Back</b> and try again.\n";
PutTrailer($localtrailer);
exit;
}
if (!is_available_username($user)) {
print "The user '$user' does already exist. Please press\n";
print "<b>Back</b> and try again.\n";
PutTrailer($localtrailer);
exit;
# Only members of the editusers group may change bless grants.
# Skip silently if this is not the case.
if ($editusers) {
my $oldgroupid = $cgi->param("oldbless_$id") || '0';
my $groupid = $cgi->param("bless_$id") || '0';
if ($groupid ne $oldgroupid) {
if ($groupid eq '0') {
$sth_remove_mapping->execute(
$otherUserID, $id, 1, GRANT_DIRECT);
push(@groupsDeniedRightsToBless, $$groups{$id}{'name'});
} else {
$sth_add_mapping->execute(
$otherUserID, $id, 1, GRANT_DIRECT);
push(@groupsGrantedRightsToBless, $$groups{$id}{'name'});
}
}
}
}
my $passworderror = ValidatePassword($password);
if ( $passworderror ) {
print $passworderror;
PutTrailer($localtrailer);
exit;
if (@groupsAddedTo || @groupsRemovedFrom) {
$dbh->do(qq{INSERT INTO profiles_activity (
userid, who,
profiles_when, fieldid,
oldvalue, newvalue
) VALUES (
?, ?, now(), ?, ?, ?
)
},
undef,
($otherUserID, $userid,
GetFieldID('bug_group'),
join(', ', @groupsRemovedFrom), join(', ', @groupsAddedTo)));
$dbh->do('UPDATE profiles SET refreshed_when=? WHERE userid = ?',
undef, ('1900-01-01 00:00:00', $otherUserID));
}
# Add the new user
SendSQL("INSERT INTO profiles ( " .
"login_name, cryptpassword, realname, " .
"emailflags, disabledtext" .
" ) VALUES ( " .
SqlQuote($user) . "," .
SqlQuote(bz_crypt($password)) . "," .
SqlQuote($realname) . "," .
SqlQuote(Bugzilla::Constants::DEFAULT_EMAIL_SETTINGS) . "," .
SqlQuote($disabledtext) . ")" );
#+++ send e-mail away
print "OK, done.<br>\n";
my $newuserid = $dbh->bz_last_key('profiles', 'userid');
my $changeduser = new Bugzilla::User($newuserid);
$changeduser->derive_groups();
print "To change ${user}'s permissions, go back and " .
"<a href=\"editusers.cgi?action=edit&user=" . url_quote($user) .
"\">edit</a> this user.";
print "<p>\n";
PutTrailer($localtrailer);
exit;
}
#
# action='del' -> ask if user really wants to delete
#
# (next action would be 'delete')
#
if ($action eq 'del') {
$candelete || ThrowUserError("users_deletion_disabled");
$editall || ThrowUserError("auth_failure", {group => "editusers",
# FIXME: should create profiles_activity entries for blesser changes.
$dbh->bz_unlock_tables();
# FIXME: userDataToVars may be off when editing ourselves.
userDataToVars($otherUserID);
$vars->{'message'} = 'account_updated';
$vars->{'loginold'} = $loginold;
$vars->{'changed_fields'} = \@changedFields;
$vars->{'groups_added_to'} = \@groupsAddedTo;
$vars->{'groups_removed_from'} = \@groupsRemovedFrom;
$vars->{'groups_granted_rights_to_bless'} = \@groupsGrantedRightsToBless;
$vars->{'groups_denied_rights_to_bless'} = \@groupsDeniedRightsToBless;
$template->process('admin/users/edit.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'del') {
my $otherUser = new Bugzilla::User($cgi->param('userid'))
|| ThrowCodeError('invalid_user_id', {'userid' => $cgi->param('userid')});
my $otherUserID = $otherUser->id();
Param('allowuserdeletion') || ThrowUserError('users_deletion_disabled');
$editusers || ThrowUserError('auth_failure', {group => "editusers",
action => "delete",
object => "users"});
canSeeUser($otherUserID) || ThrowUserError('auth_failure',
{reason => "not_visible",
action => "delete",
object => "users"});
CheckUser($user);
# display some data about the user
SendSQL("SELECT userid, realname FROM profiles
WHERE login_name=" . SqlQuote($user));
my ($thisuserid, $realname) =
FetchSQLData();
$realname = ($realname ? html_quote($realname) : "<FONT COLOR=\"red\">missing</FONT>");
PutHeader("Delete user $user");
print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>\n";
print "<TR BGCOLOR=\"#6666FF\">\n";
print " <TH VALIGN=\"top\" ALIGN=\"left\">Part</TH>\n";
print " <TH VALIGN=\"top\" ALIGN=\"left\">Value</TH>\n";
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">Login name:</TD>\n";
print " <TD VALIGN=\"top\">$user</TD>\n";
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">Real name:</TD>\n";
print " <TD VALIGN=\"top\">$realname</TD>\n";
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">Group set:</TD>\n";
print " <TD VALIGN=\"top\">";
SendSQL("SELECT name
FROM groups, user_group_map
WHERE groups.id = user_group_map.group_id
AND user_group_map.user_id = $thisuserid
AND isbless = 0
ORDER BY name");
my $found = 0;
while ( MoreSQLData() ) {
my ($name) = FetchSQLData();
print "<br>\n" if $found;
print ucfirst $name;
$found = 1;
}
print "none" unless $found;
print "</TD>\n</TR>";
# Check if the user is an initialowner
my $nodelete = '';
SendSQL("SELECT products.name, components.name " .
"FROM products, components " .
"WHERE products.id = components.product_id " .
" AND initialowner=" . login_to_id($user));
$found = 0;
while (MoreSQLData()) {
if ($found) {
print "<BR>\n";
} else {
print "<TR>\n";
print " <TD VALIGN=\"top\">Initial owner:</TD>\n";
print " <TD VALIGN=\"top\">";
}
my ($product, $component) = FetchSQLData();
print "<a href=\"editcomponents.cgi?product=", url_quote($product),
"&component=", url_quote($component),
"&action=edit\">$product: $component</a>";
$found = 1;
$nodelete = 'initial bug owner';
}
print "</TD>\n</TR>" if $found;
# Check if the user is an initialqacontact
SendSQL("SELECT products.name, components.name " .
"FROM products, components " .
"WHERE products.id = components.product_id " .
" AND initialqacontact=" . login_to_id($user));
$found = 0;
while (MoreSQLData()) {
if ($found) {
print "<BR>\n";
} else {
print "<TR>\n";
print " <TD VALIGN=\"top\">Initial QA contact:</TD>\n";
print " <TD VALIGN=\"top\">";
}
my ($product, $component) = FetchSQLData();
print "<a href=\"editcomponents.cgi?product=", url_quote($product),
"&component=", url_quote($component),
"&action=edit\">$product: $component</a>";
$found = 1;
$nodelete = 'initial QA contact';
object => "user"});
$vars->{'otheruser'} = $otherUser;
$vars->{'editcomponents'} = UserInGroup('editcomponents');
# If the user is initial owner or initial QA contact of a component,
# then no deletion is possible.
$vars->{'product_responsibilities'} = productResponsibilities($otherUserID);
# Find other cross references.
$vars->{'bugs'} = $dbh->selectrow_array(
qq{SELECT COUNT(*)
FROM bugs
WHERE assigned_to = ? OR
qa_contact = ? OR
reporter = ?
},
undef, ($otherUserID, $otherUserID, $otherUserID));
$vars->{'cc'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM cc WHERE who = ?',
undef, $otherUserID);
$vars->{'bugs_activity'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM bugs_activity WHERE who = ?',
undef, $otherUserID);
$vars->{'flags'}{'requestee'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM flags WHERE requestee_id = ?',
undef, $otherUserID);
$vars->{'flags'}{'setter'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM flags WHERE setter_id = ?',
undef, $otherUserID);
$vars->{'groups'} = $dbh->selectall_arrayref(
qq{SELECT name
FROM groups, user_group_map
WHERE id = group_id
AND user_id = ?
AND isbless = 0
ORDER BY name
},
{'Slice' => {}}, $otherUserID);
$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->{'profiles_activity'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM profiles_activity WHERE who = ? AND userid != ?',
undef, ($otherUserID, $otherUserID));
$vars->{'series'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM series WHERE creator = ?',
undef, $otherUserID);
$vars->{'votes'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM votes WHERE who = ?',
undef, $otherUserID);
$vars->{'watch'}{'watched'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM watch WHERE watched = ?',
undef, $otherUserID);
$vars->{'watch'}{'watcher'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM watch WHERE watcher = ?',
undef, $otherUserID);
$vars->{'whine_events'} = $dbh->selectrow_array(
'SELECT COUNT(*) FROM whine_events WHERE owner_userid = ?',
undef, $otherUserID);
$vars->{'whine_schedules'} = $dbh->selectrow_array(
qq{SELECT COUNT(distinct eventid)
FROM whine_schedules
WHERE mailto = ?
AND mailto_type = ?
},
undef, ($otherUserID, MAILTO_USER));
$template->process('admin/users/confirm-delete.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
###########################################################################
} elsif ($action eq 'delete') {
my $otherUser = new Bugzilla::User($cgi->param('userid'))
|| ThrowCodeError('invalid_user_id', {'userid' => $cgi->param('userid')});
my $otherUserID = $otherUser->id();
my $otherUserLogin = $otherUser->login();
# Lock tables during the check+removal session.
# FIXME: if there was some change on these tables after the deletion
# confirmation checks, we may do something here we haven't warned
# about.
$dbh->bz_lock_tables('products READ',
'components READ',
'logincookies WRITE',
'profiles WRITE',
'profiles_activity WRITE',
'groups READ',
'user_group_map WRITE',
'group_group_map READ',
'flags WRITE',
'cc WRITE',
'namedqueries WRITE',
'tokens WRITE',
'votes WRITE',
'watch WRITE',
'series WRITE',
'series_data WRITE',
'whine_schedules WRITE',
'whine_queries WRITE',
'whine_events WRITE');
Param('allowuserdeletion')
|| ThrowUserError('users_deletion_disabled', undef, 'abort');
$editusers || ThrowUserError('auth_failure',
{group => "editusers",
action => "delete",
object => "users"},
'abort');
canSeeUser($otherUserID) || ThrowUserError('auth_failure',
{reason => "not_visible",
action => "delete",
object => "user"},
'abort');
productResponsibilities($otherUserID)
&& ThrowUserError('user_has_responsibility', undef, 'abort');
Bugzilla->logout_user_by_id($otherUserID);
# Reference removals.
$dbh->do('UPDATE flags set requestee_id = NULL WHERE requestee_id = ?',
undef, $otherUserID);
# Simple deletions in referred tables.
$dbh->do('DELETE FROM cc WHERE who = ?', undef, $otherUserID);
$dbh->do('DELETE FROM logincookies WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM namedqueries WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM profiles_activity WHERE userid = ? OR who = ?', undef,
($otherUserID, $otherUserID));
$dbh->do('DELETE FROM tokens WHERE userid = ?', undef, $otherUserID);
$dbh->do('DELETE FROM user_group_map WHERE user_id = ?', undef,
$otherUserID);
$dbh->do('DELETE FROM votes WHERE who = ?', undef, $otherUserID);
$dbh->do('DELETE FROM watch WHERE watcher = ? OR watched = ?', undef,
($otherUserID, $otherUserID));
# More complex deletions in referred tables.
my $id;
# 1) Series
my $sth_seriesid = $dbh->prepare(
'SELECT series_id FROM series WHERE creator = ?');
my $sth_deleteSeries = $dbh->prepare(
'DELETE FROM series WHERE series_id = ?');
my $sth_deleteSeriesData = $dbh->prepare(
'DELETE FROM series_data WHERE series_id = ?');
$sth_seriesid->execute($otherUserID);
while ($id = $sth_seriesid->fetchrow_array()) {
$sth_deleteSeriesData->execute($id);
$sth_deleteSeries->execute($id);
}
print "</TD>\n</TR>" if $found;
print "</TABLE>\n";
# 2) Whines
my $sth_whineidFromSchedules = $dbh->prepare(
qq{SELECT eventid FROM whine_schedules
WHERE mailto = ? AND mailto_type = ?});
my $sth_whineidFromEvents = $dbh->prepare(
'SELECT id FROM whine_events WHERE owner_userid = ?');
my $sth_deleteWhineEvent = $dbh->prepare(
'DELETE FROM whine_events WHERE id = ?');
my $sth_deleteWhineQuery = $dbh->prepare(
'DELETE FROM whine_queries WHERE eventid = ?');
my $sth_deleteWhineSchedule = $dbh->prepare(
'DELETE FROM whine_schedules WHERE eventid = ?');
$sth_whineidFromSchedules->execute($otherUserID, MAILTO_USER);
while ($id = $sth_whineidFromSchedules->fetchrow_array()) {
$sth_deleteWhineQuery->execute($id);
$sth_deleteWhineSchedule->execute($id);
$sth_deleteWhineEvent->execute($id);
}
if ($nodelete) {
print "<P>You can't delete this user because '$user' is an $nodelete ",
"for at least one product.";
PutTrailer($localtrailer);
exit;
$sth_whineidFromEvents->execute($otherUserID);
while ($id = $sth_whineidFromEvents->fetchrow_array()) {
$sth_deleteWhineQuery->execute($id);
$sth_deleteWhineSchedule->execute($id);
$sth_deleteWhineEvent->execute($id);
}
# Finally, remove the user account itself.
$dbh->do('DELETE FROM profiles WHERE userid = ?', undef, $otherUserID);
print "<H2>Confirmation</H2>\n";
print "<P>Do you really want to delete this user?<P>\n";
$dbh->bz_unlock_tables();
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Yes, delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"user\" VALUE=\"$user\">\n";
print "</FORM>";
$vars->{'message'} = 'account_deleted';
$vars->{'otheruser'}{'login'} = $otherUserLogin;
$vars->{'restrictablegroups'} = groupsUserMayBless($user, 'id', 'name');
$template->process('admin/users/search.html.tmpl', $vars)
|| ThrowTemplateError($template->error());
PutTrailer($localtrailer);
exit;
###########################################################################
} else {
$vars->{'action'} = $action;
ThrowCodeError('action_unrecognized', $vars);
}
exit;
###########################################################################
# Helpers
###########################################################################
#
# action='delete' -> really delete the user
#
if ($action eq 'delete') {
$candelete || ThrowUserError("users_deletion_disabled");
$editall || ThrowUserError("auth_failure", {group => "editusers",
action => "delete",
object => "users"});
CheckUser($user);
SendSQL("SELECT userid
FROM profiles
WHERE login_name=" . SqlQuote($user));
my $userid = FetchOneColumn();
Bugzilla->logout_user_by_id($userid);
SendSQL("DELETE FROM profiles
WHERE login_name=" . SqlQuote($user));
SendSQL("DELETE FROM user_group_map
WHERE user_id=" . $userid);
PutHeader("Deleting user");
print "User deleted.<BR>\n";
PutTrailer($localtrailer);
exit;
# Copy incoming list selection values from CGI params to template variables.
sub mirrorListSelectionValues {
if (defined($cgi->param('matchtype'))) {
foreach ('matchstr', 'matchtype', 'grouprestrict', 'groupid') {
$vars->{'listselectionvalues'}{$_} = $cgi->param($_);
}
}
}
# Give a list of IDs of groups the user can see.
sub visibleGroupsAsString {
return join(', ', @{$user->visible_groups_direct()});
}
# Give a list of IDs of groups the user may bless.
sub groupsUserMayBless {
my $user = shift;
my $fieldList = join(', ', @_);
my $query;
my $connector;
my @bindValues;
#
# action='edit' -> present the user edit from
#
# (next action would be 'update')
#
if ($action eq 'edit') {
PutHeader("Edit user $user");
CheckUser($user);
# get data of user
SendSQL("SELECT userid, realname, disabledtext
FROM profiles
WHERE login_name=" . SqlQuote($user));
my ($thisuserid, $realname, $disabledtext) = FetchSQLData();
$user->derive_groups(1);
if ($thisuserid > 0) {
# Force groups to be up to date
my $changeduser = new Bugzilla::User($thisuserid);
$changeduser->derive_groups();
}
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements($thisuserid, $user, $realname, $disabledtext);
print "</TR></TABLE>\n";
print "<INPUT TYPE=HIDDEN NAME=\"userold\" VALUE=\"$user\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"realnameold\" VALUE=\"$realname\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"disabledtextold\" VALUE=\"" .
value_quote($disabledtext) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"update\">\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Update\">\n";
print "<BR>User is a member of any groups shown with a check or grey bar.
A grey bar indicates indirect membership, either derived from other
groups (marked with square brackets) or via regular expression
(marked with '*').<p>
Square brackets around the bless checkbox indicate the ability
to bless users (grant them membership in the group) as a result
of membership in another group.
<BR>";
print "</FORM>";
if ($candelete) {
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Delete User\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"del\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"user\" VALUE=\"$user\">\n";
print "</FORM>";
if ($editusers) {
$query = "SELECT DISTINCT $fieldList FROM groups";
$connector = 'WHERE';
} else {
$query = qq{SELECT DISTINCT $fieldList
FROM groups, user_group_map AS ugm
LEFT JOIN group_group_map AS ggm
ON ggm.member_id = ugm.group_id
AND ggm.grant_type = ?
WHERE user_id = ?
AND ((id = group_id AND isbless = 1) OR
(id = grantor_id))
};
@bindValues = (GROUP_BLESS, $userid);
$connector = 'AND';
}
my $x = $localtrailer;
$x =~ s/more/other/;
PutTrailer($x);
exit;
}
#
# action='update' -> update the user
#
if ($action eq 'update') {
my $userold = trim($::FORM{userold} || '');
my $realname = trim($::FORM{realname} || '');
my $realnameold = trim($::FORM{realnameold} || '');
my $password = $::FORM{password} || '';
my $disabledtext = trim($::FORM{disabledtext} || '');
my $disabledtextold = trim($::FORM{disabledtextold} || '');
my @localtrailers = ($localtrailer);
$localtrailer = qq|<a href="editusers.cgi?action=edit&user=XXX">edit user again</a>|;
PutHeader("Updating user $userold" . ($realnameold && " ($realnameold)"));
CheckUser($userold);
SendSQL("SELECT userid FROM profiles
WHERE login_name=" . SqlQuote($userold));
my ($thisuserid) = FetchSQLData();
my $emailregexp = Param("emailregexp");
unless ($user =~ m/$emailregexp/) {
print "The user name entered must be a valid e-mail address. Please press\n";
print "<b>Back</b> and try again.\n";
PutTrailer($localtrailer);
exit;
# If visibilitygroups are used, restrict the set of groups.
if (Param('usevisibilitygroups')) {
my $visibleGroups = visibleGroupsAsString();
$query .= " $connector id in ($visibleGroups)";
}
my @grpadd = ();
my @grpdel = ();
my $chggrp = 0;
SendSQL("SELECT id, name FROM groups");
while (my ($groupid, $name) = FetchSQLData()) {
next unless ($editall || Bugzilla->user->can_bless($name));
if ($::FORM{"oldgroup_$groupid"} != ($::FORM{"group_$groupid"} ? 1 : 0)) {
# group membership changed
PushGlobalSQLState();
$chggrp = 1;
SendSQL("DELETE FROM user_group_map
WHERE user_id = $thisuserid
AND group_id = $groupid
AND isbless = 0
AND grant_type = " . GRANT_DIRECT);
if ($::FORM{"group_$groupid"}) {
SendSQL("INSERT INTO user_group_map
(user_id, group_id, isbless, grant_type)
VALUES ($thisuserid, $groupid, 0," . GRANT_DIRECT . ")");
print "Added user to group $name<BR>\n";
push(@grpadd, $name);
} else {
print "Dropped user from group $name<BR>\n";
push(@grpdel, $name);
}
PopGlobalSQLState();
}
if ($editall && ($::FORM{"oldbless_$groupid"} != ($::FORM{"bless_$groupid"} ? 1 : 0))) {
# group membership changed
PushGlobalSQLState();
SendSQL("DELETE FROM user_group_map
WHERE user_id = $thisuserid
AND group_id = $groupid
AND isbless = 1
AND grant_type = " . GRANT_DIRECT);
if ($::FORM{"bless_$groupid"}) {
SendSQL("INSERT INTO user_group_map
(user_id, group_id, isbless, grant_type)
VALUES ($thisuserid, $groupid, 1," . GRANT_DIRECT . ")");
print "Granted user permission to bless group $name<BR>\n";
} else {
print "Revoked user's permission to bless group $name<BR>\n";
}
PopGlobalSQLState();
}
}
my $fieldid = GetFieldID("bug_group");
if ($chggrp) {
SendSQL("INSERT INTO profiles_activity " .
"(userid, who, profiles_when, fieldid, oldvalue, newvalue) " .
"VALUES " . "($thisuserid, $::userid, now(), $fieldid, " .
SqlQuote(join(", ",@grpdel)) . ", " .
SqlQuote(join(", ",@grpadd)) . ")");
SendSQL("UPDATE profiles SET refreshed_when='1900-01-01 00:00:00' " .
"WHERE userid = $thisuserid");
}
$query .= ' ORDER BY name';
return $dbh->selectall_arrayref($query, {'Slice' => {}}, @bindValues);
}
# Update the database with the user's new password if they changed it.
if ( $editall && $password ) {
my $passworderror = ValidatePassword($password);
if ( !$passworderror ) {
my $cryptpassword = SqlQuote(bz_crypt($password));
my $loginname = SqlQuote($userold);
SendSQL("UPDATE profiles
SET cryptpassword = $cryptpassword
WHERE login_name = $loginname");
SendSQL("SELECT userid
FROM profiles
WHERE login_name=" . SqlQuote($userold));
my $userid = FetchOneColumn();
Bugzilla->logout_user_by_id($userid);
print "Updated password.<BR>\n";
} else {
print "Did not update password: $passworderror<br>\n";
}
}
if ($editall && $realname ne $realnameold) {
SendSQL("UPDATE profiles
SET realname=" . SqlQuote($realname) . "
WHERE login_name=" . SqlQuote($userold));
print 'Updated real name to <q>' . html_quote($realname) . "</q>.<BR>\n";
}
if ($editall && $disabledtext ne $disabledtextold) {
SendSQL("UPDATE profiles
SET disabledtext=" . SqlQuote($disabledtext) . "
WHERE login_name=" . SqlQuote($userold));
SendSQL("SELECT userid
FROM profiles
WHERE login_name=" . SqlQuote($userold));
my $userid = FetchOneColumn();
Bugzilla->logout_user_by_id($userid);
print "Updated disabled text.<BR>\n";
# Determine whether the user can see a user. (Checks for existence, too.)
sub canSeeUser {
my $otherUserID = shift;
my $query;
if (Param('usevisibilitygroups')) {
my $visibleGroups = visibleGroupsAsString();
$query = qq{SELECT COUNT(DISTINCT userid)
FROM profiles, user_group_map
WHERE userid = ?
AND user_id = userid
AND isbless = 0
AND group_id IN ($visibleGroups)
};
} else {
$query = qq{SELECT COUNT(userid)
FROM profiles
WHERE userid = ?
};
}
if ($editall && $user ne $userold) {
unless ($user) {
print "Sorry, I can't delete the user's name.";
$userold = url_quote($userold);
$localtrailer =~ s/XXX/$userold/;
push @localtrailers, $localtrailer;
PutTrailer(@localtrailers);
exit;
}
if (TestUser($user)) {
print "Sorry, user name '$user' is already in use.";
$userold = url_quote($userold);
$localtrailer =~ s/XXX/$userold/;
push @localtrailers, $localtrailer;
PutTrailer($localtrailer);
exit;
}
SendSQL("UPDATE profiles
SET login_name=" . SqlQuote($user) . "
WHERE login_name=" . SqlQuote($userold));
return $dbh->selectrow_array($query, undef, $otherUserID);
}
print q|Updated user's name to <a href="mailto:| .
url_quote($user) . '">' . html_quote($user) . "</a>.<BR>\n";
# Retrieve product responsibilities, usable for both display and verification.
sub productResponsibilities {
my $userid = shift;
my $h = $dbh->selectall_arrayref(
qq{SELECT products.name AS productname,
components.name AS componentname,
initialowner,
initialqacontact
FROM products, components
WHERE products.id = components.product_id
AND ? IN (initialowner, initialqacontact)
},
{'Slice' => {}}, $userid);
if (@$h) {
return $h;
} else {
return undef;
}
my $changeduser = new Bugzilla::User($thisuserid);
$changeduser->derive_groups();
$user = url_quote($user);
$localtrailer =~ s/XXX/$user/;
push @localtrailers, $localtrailer;
PutTrailer(@localtrailers);
exit;
}
#
# No valid action found
#
PutHeader("Error");
print "I don't have a clue what you want.<BR>\n";
# Retrieve user data for the user editing form. User creation and user
# editing code rely on this to call derive_groups().
sub userDataToVars {
my $userid = shift;
my $user = new Bugzilla::User($userid);
my $query;
$user->derive_groups();
$vars->{'otheruser'} = $user;
$vars->{'groups'} = groupsUserMayBless($user, 'id', 'name', 'description');
$vars->{'disabledtext'} = $dbh->selectrow_array(
'SELECT disabledtext FROM profiles WHERE userid = ?', undef, $userid);
$vars->{'permissions'} = $dbh->selectall_hashref(
qq{SELECT id,
COUNT(directmember.group_id) AS directmember,
COUNT(regexpmember.group_id) AS regexpmember,
COUNT(derivedmember.group_id) AS derivedmember,
COUNT(directbless.group_id) AS directbless
FROM groups
LEFT JOIN user_group_map AS directmember
ON directmember.group_id = id
AND directmember.user_id = ?
AND directmember.isbless = 0
AND directmember.grant_type = ?
LEFT JOIN user_group_map AS regexpmember
ON regexpmember.group_id = id
AND regexpmember.user_id = ?
AND regexpmember.isbless = 0
AND regexpmember.grant_type = ?
LEFT JOIN user_group_map AS derivedmember
ON derivedmember.group_id = id
AND derivedmember.user_id = ?
AND derivedmember.isbless = 0
AND derivedmember.grant_type = ?
LEFT JOIN user_group_map AS directbless
ON directbless.group_id = id
AND directbless.user_id = ?
AND directbless.isbless = 1
AND directbless.grant_type = ?
GROUP BY id
},
'id', undef,
($userid, GRANT_DIRECT,
$userid, GRANT_REGEXP,
$userid, GRANT_DERIVED,
$userid, GRANT_DIRECT));
# Find indirect bless permission.
$query = qq{SELECT groups.id
FROM groups, user_group_map AS ugm, group_group_map AS ggm
WHERE ugm.user_id = ?
AND groups.id = ggm.grantor_id
AND ggm.member_id = ugm.group_id
AND ugm.isbless = 0
AND ggm.grant_type = ?
GROUP BY id
};
foreach (@{$dbh->selectall_arrayref($query, undef, ($userid, GROUP_BLESS))}) {
# Merge indirect bless permissions into permission variable.
$vars->{'permissions'}{${$_}[0]}{'indirectbless'} = 1;
}
}
......@@ -613,11 +613,11 @@ sub ValidatePassword {
my ($password, $matchpassword) = @_;
if (length($password) < 3) {
ThrowUserError("password_too_short");
ThrowUserError("password_too_short", undef, 'abort');
} elsif (length($password) > 16) {
ThrowUserError("password_too_long");
ThrowUserError("password_too_long", undef, 'abort');
} elsif ((defined $matchpassword) && ($password ne $matchpassword)) {
ThrowUserError("passwords_dont_match");
ThrowUserError("passwords_dont_match", undef, 'abort');
}
}
......
/* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Bugzilla Bug Tracking System.
*
* Contributor(s): Marc Schumann <wurblzap@gmail.com>
*/
ul.warningmessages {
background-color: white;
border-style: solid;
border-width: 1px;
border-color: yellow;
padding: 1ex 1ex 1ex 4ex;
}
p.areyoureallyreallysure {
color: red;
font-size: 120%;
font-weight: bold;
}
/* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Bugzilla Bug Tracking System.
*
* Contributor(s): Marc Schumann <wurblzap@gmail.com>
*/
table.main {
border-spacing: 1em;
}
table.main tr {
vertical-align: top;
border-top: solid thin black;
}
table.main th {
text-align: right;
white-space: nowrap;
}
table.main th,
table.main td {
padding: 0;
}
table.main ul {
list-style-type: none;
padding-left: 0
}
table.groups {
border-spacing: 1px;
}
table.groups tr.indirect {
background-color: #cccccc;
}
table.groups th {
text-align: left;
padding: 0 0 0 1ex;
}
table.groups td {
padding: 2px;
}
table.groups td.checkbox {
text-align: center;
white-space: nowrap;
}
......@@ -56,7 +56,7 @@
<p><b>One or more users belong to this group. You cannot delete
this group while there are users in it.</b>
<br><a href="editusers.cgi?action=list&group=[% gid FILTER html %]">Show
<br><a href="editusers.cgi?action=list&group=[% gid FILTER html %]&grouprestrict=1">Show
me which users</a> - <input type="checkbox" name="removeusers">Remove
all users from this group for me.</p>
[% END %]
......
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# listselectionvalues: selection values to recreate the current user
# list.
# editusers: is viewing user member of editusers?
# editcomponents: is viewing user member of editcomponents?
# otheruser: Bugzilla::User object of the viewed user.
# groups: array of Group names the viewed user is a member
# of.
# product_responsibilities: list of hashes, one entry per Bugzilla component.
# productname: Name of the product.
# componentname: Name of the component.
# initialowner: User ID of initial owner.
# initialqacontact: User ID of initial QA contact.
# bugs: number of bugs the viewed user has a role in
# bug_activity: number of bugs the viewed user has activity
# entries on
# cc number of bugs the viewed user is cc list member
# of
# 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
# 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
# watch.watched: number of users the viewed user is being watched
# by
# watch.watcher: number of users the viewed user is watching
# whine_events: number of whine events the viewed user has created
# whine_schedules: number of whine schedules the viewed user has
# created
#%]
[% PROCESS global/header.html.tmpl
title = "Confirm deletion of user $otheruser.login"
style_urls = ['skins/standard/admin.css',
'skins/standard/editusers.css']
%]
[% PROCESS admin/users/listselectvars.html.tmpl
listselectionvalues = listselectionvalues
%]
[% responsibilityterms = {
'initialowner' => 'Initial Owner',
'initialqacontact' => 'Initial QA Contact'
}
%]
<table class="main">
<tr>
<th>Login name:</th>
<td>[% otheruser.login FILTER html %]</td>
</tr>
<tr>
<th>Real name:</th>
<td>[% otheruser.name FILTER html %]</td>
</tr>
<tr>
<th>Group set:</th>
<td>
[% IF groups.size %]
<ul>
[% FOREACH group = groups %]
<li>[% group.name FILTER html %]</li>
[% END %]
</ul>
[% ELSE %]
None
[% END %]
</td>
</tr>
[% IF product_responsibilities.size %]
<tr>
<th>Product responsibilities:</th>
<td>
<ul>
[% FOREACH component = product_responsibilities %]
<li>
[% andstring = '' %]
[% FOREACH responsibility = ['initialowner', 'initialqacontact'] %]
[% IF component.$responsibility == userid %]
[% andstring %] [% responsibilityterms.$responsibility %]
[% andstring = ' and ' %]
[% END %]
[% END %]
for
[% IF editcomponents %]
<a href="editcomponents.cgi?action=edit&amp;product=
[% component.productname FILTER url_quote %]&amp;component=
[% component.componentname FILTER url_quote %]">
[% END %]
[%+ component.productname FILTER html %]:
[% component.componentname FILTER html %]
[% IF editcomponents %]
</a>
[% END %]
</li>
[% END %]
</ul>
</td>
</tr>
[% END %]
</table>
[% IF product_responsibilities.size %]
<p>
You can't delete this user at this time because
[%+ otheruser.login FILTER html %] has got responsibilities for at least
one product.
</p>
<p>
[% IF editcomponents %]
Change this by clicking the product editing links above,
[% ELSE %]
For now, you can
[% END %]
[% ELSE %]
<h2>Confirmation</h2>
[% IF bugs || bug_activity || cc || flags.requestee || flags.setter ||
longdescs || namedqueries || profiles_activity || series || votes ||
watch.watched || watch.watcher || whine_events || whine_schedules %]
<ul class="warningmessages">
[% IF bugs %]
<li>
[% otheruser.login FILTER html %]
<a href="buglist.cgi?emailassigned_to1=1&amp;emailreporter1=1&amp;emailqa_contact1=1&amp;emailtype1=exact&amp;email1=[% otheruser.login FILTER url_quote %]">is
related to
[% IF bugs == 1 %]
[%+ terms.abug %]
[% ELSE %]
[%+ bugs %] [%+ terms.bugs %]
[% END %]</a>, by having reported, being assigned to or being
the QA contact.
If you delete the user account, the [% terms.bugs %] table in the
database will be inconsistent, resulting in
[% IF bugs == 1 %]
this [% terms.bug %]
[% ELSE %]
these [% terms.bugs %]
[% END %]
not appearing in [% terms.bug %] lists any more.
</li>
[% END %]
[% IF bugs_activity %]
<li>
[% otheruser.login FILTER html %] has made
[% IF bugs_activity == 1 %]
a change on [% terms.abug %]
[% ELSE %]
changes on [% terms.bugs %]
[% END %].
If you delete the user account, the [% terms.bugs %] activity table in
the database will be inconsistent, resulting in
[% IF bugs_activity == 1 %]
this change
[% ELSE %]
these changes
[% END %]
not showing up in [% terms.bug %] activity logs any more.
</li>
[% END %]
[% IF cc %]
<li>
[% otheruser.login FILTER html %]
<a href="buglist.cgi?emailcc1=1&amp;emailtype1=exact&amp;email1=[% otheruser.login FILTER url_quote %]">is
on the CC list of
[% IF cc == 1 %]
[%+ terms.abug %]
[% ELSE %]
[%+ cc %] [%+ terms.bugs %]
[% END %]</a>.
If you delete the user account, it will be removed from these CC
lists.
</li>
[% END %]
[% IF flags.requestee %]
<li>
[% otheruser.login FILTER html %] has been
<a href="buglist.cgi?field0-0-0=requestees.login_name&amp;type0-0-0=equals&amp;value0-0-0=[% otheruser.login FILTER url_quote %]">asked
to set
[% IF flags.requestee == 1 %]
a flag
[% ELSE %]
[% flags.requestee %] flags
[% END %]</a>.
If you delete the user account,
[% IF flags.requestee == 1 %]
this flag
[% ELSE %]
these flags
[% END %]
will change to be unspecifically requested.
</li>
[% END %]
[% IF flags.setter %]
<li>
[% otheruser.login FILTER html %] has
<a href="buglist.cgi?field0-0-0=setters.login_name&amp;type0-0-0=equals&amp;value0-0-0=[% otheruser.login FILTER url_quote %]">set
or requested
[% IF flags.setter == 1 %]
a flag
[% ELSE %]
[%+ flags.setter %] flags
[% END %]</a>.
If you delete the user account, the flags table in the database
will be inconsistent, resulting in
[% IF flags.setter == 1 %]
this flag
[% ELSE %]
these flags
[% END %]
not displaying correctly any more.
</li>
[% END %]
[% IF longdescs %]
<li>
[% otheruser.login FILTER html %] has
<a href="buglist.cgi?emaillongdesc1=1&amp;emailtype1=exact&amp;email1=[% otheruser.login FILTER url_quote %]">commented
[% IF longdescs == 1 %]
once on [% terms.abug %]
[% ELSE %]
[%+ longdescs %] times on [% terms.bugs %]
[% END %]</a>.
If you delete the user account, the comments table in the database
will be inconsistent, resulting in
[% IF longdescs == 1 %]
this comment
[% ELSE %]
these comments
[% END %]
not being visible any more.
</li>
[% END %]
[% IF namedqueries %]
<li>
[% otheruser.login FILTER html %]
has
[% IF namedqueries == 1 %]
a named query
[% ELSE %]
[%+ namedqueries %] named queries
[% END %].
[% IF namedqueries == 1 %]
This named query
[% ELSE %]
These named queries
[% END %]
will be deleted along with the user account.
</li>
[% END %]
[% IF profiles_activity %]
<li>
[% otheruser.login FILTER html %] has made
[% IF bugs_activity == 1 %]
a change on a other user's profile
[% ELSE %]
changes on other users' profiles
[% END %].
If you delete the user account, the user profiles activity table in
the database will be inconsistent.
</li>
[% END %]
[% IF series %]
<li>
[% otheruser.login FILTER html %] has created
[% IF series == 1 %]
a series
[% ELSE %]
[%+ series %] series
[% END %].
[% IF series == 1 %]
This series
[% ELSE %]
These series
[% END %]
will be deleted along with the user account.
</li>
[% END %]
[% IF votes %]
<li>
[% otheruser.login FILTER html %] has voted on
[% IF votes == 1 %]
[%+ terms.abug %]
[% ELSE %]
[%+ votes %] [%+ terms.bugs %]
[% END %].
If you delete the user account,
[% IF votes == 1 %]
this vote
[% ELSE %]
these votes
[% END %]
will be deleted along with the user account.
</li>
[% END %]
[% IF watch.watched || watch.watcher %]
<li>
[% otheruser.login FILTER html %]
[% IF watch.watched %]
is being watched by
[% IF watch.watched == 1 %]
a user
[% ELSE %]
[%+ watch.watched %] users
[% END %]
[% END %]
[% IF watch.watcher %]
[%+ 'and' IF watch.watched %]
watches
[% IF watch.watcher == 1 %]
a user
[% ELSE %]
[%+ watch.watcher %] users
[% END %]
[% END %].
[% IF watch.watched + watch.watcher == 1 %]
This watching
[% ELSE %]
These watchings
[% END %]
will cease along with the deletion of the user account.
</li>
[% END %]
[% IF whine_events || whine_schedules %]
<li>
[% otheruser.login FILTER html %]
[% IF whine_events %]
has scheduled
[% IF whine_events == 1 %]
a whine
[% ELSE %]
[%+ whine_events %] whines
[% END %]
[% END %]
[% IF whine_schedules %]
[%+ 'and' IF whine_events %]
is on the receiving end of
[% IF whine_schedules == 1 %]
a whine
[% ELSE %]
[%+ whine_schedules %] whines
[% END %]
[% END %].
[% IF whine_events + whine_schedules == 1 %]
This whine
[% ELSE %]
These whines
[% END %]
will be deleted along with the user account.
</li>
[% END %]
</ul>
<p class="areyoureallyreallysure">
Please be aware of the consequences of this before continuing.
</p>
[% END %]
<p>Do you really want to delete this user account?</p>
<form method="post" action="editusers.cgi">
<p>
<input type="submit" value="Yes, delete" />
<input type="hidden" name="action" value="delete" />
<input type="hidden" name="userid" value="[% otheruser.id %]" />
[% INCLUDE listselectionhiddenfields %]
</p>
</form>
<p>If you do not want to delete the user account at this time,
[% END %]
<a href="editusers.cgi?action=edit&amp;userid=[% otheruser.id %]
[% INCLUDE listselectionurlparams %]">edit the user</a>,
go
<a href="editusers.cgi?action=list[% INCLUDE listselectionurlparams %]">back
to the user list</a>,
[% IF editusers %]
<a href="editusers.cgi?action=add[% INCLUDE listselectionurlparams %]">add
a new user</a>,
[% END %]
or <a href="editusers.cgi">find other users</a>.
</p>
[% PROCESS global/footer.html.tmpl %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# listselectionvalues: selection values to recreate the current user list.
# editusers: is viewing user member of editusers?
#%]
[% PROCESS global/header.html.tmpl
title = "Add user"
style_urls = ['skins/standard/editusers.css']
onload = "document.forms['f'].login.focus()"
%]
[% PROCESS admin/users/listselectvars.html.tmpl
listselectionvalues = listselectionvalues
%]
<form name="f" method="post" action="editusers.cgi">
<table class="main">
[% PROCESS admin/users/userdata.html.tmpl
editform = 0
editusers = editusers
otheruser = []
%]
</table>
<p>
<input type="submit" value="Add" />
<input type="hidden" name="action" value="new" />
[% INCLUDE listselectionhiddenfields %]
</p>
</form>
<p>
You can also <a href="editusers.cgi">find a user</a>
[% IF listselectionvalues %],
or
<a href="editusers.cgi?action=list[% INCLUDE listselectionurlparams %]">go
back to the user list</a>
[% END %].
</p>
[% PROCESS global/footer.html.tmpl %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# message: message tag specifying a global/messages.html.tmpl
# message
# listselectionvalues: selection values to recreate the current user list.
# editusers: is viewing user member of editusers?
# otheruser: Bugzilla::User object of viewed user.
# groups: array of group information (name, grant type,
# canbless) for viewed user.
#%]
[% PROCESS global/header.html.tmpl
title = "Edit user $login"
message = message
style_urls = ['skins/standard/editusers.css']
%]
[% PROCESS admin/users/listselectvars.html.tmpl
listselectionvalues = listselectionvalues
%]
<form method="post" action="editusers.cgi">
<table class="main">
[% PROCESS admin/users/userdata.html.tmpl
editform = 1
editusers = editusers
otheruser = otheruser
%]
[% IF groups.size %]
<tr>
<th>Group access:</th>
<td>
<table class="groups">
<tr>
[% IF editusers %]
<th colspan="3">
Can turn these [% terms.bits %] on for other users
</th>
[% END %]
</tr>
<tr>
[% IF editusers %]
<td style="text-align: center; font-weight: bold">|</td>
[% END %]
<th colspan="2">User is a member of these groups</th>
</tr>
[% FOREACH group = groups %]
[% perms = permissions.${group.id} %]
<tr class="[% 'in' IF perms.regexpmember || perms.derivedmember %]direct">
[% IF editusers %]
<td class="checkbox">
[% '[' IF perms.indirectbless %]
[% %]<input type="checkbox"
name="bless_[% group.id %]"
value="1"
[% ' checked="checked"' IF perms.directbless %] />
[% ']' IF perms.indirectbless %]
[% %]<input type="hidden" name="oldbless_[% group.id %]"
value="[% perms.directbless %]" /></td>
[% END %]
<td class="checkbox">
[% '[' IF perms.derivedmember %]
[% '*' IF perms.regexpmember %]
[%%]<input type="checkbox"
id="group_[% group.id %]"
name="group_[% group.id %]"
value="1"
[% ' checked="checked"' IF perms.directmember %] />
[% '*' IF perms.regexpmember %]
[% ']' IF perms.derivedmember %]
[% %]<input type="hidden" name="oldgroup_[% group.id %]"
value="[% perms.directmember %]" /></td>
<td class="groupname">
<label for="group_[% group.id %]">
<strong>[% group.name FILTER html %]:</strong>
[%+ group.description FILTER html %]
</label>
</td>
</tr>
[% END %]
</table>
</td>
</tr>
[% END %]
</table>
<p>
<input type="submit" value="Update" />
<input type="hidden" name="userid" value="[% otheruser.id %]" />
<input type="hidden" name="action" value="update" />
[% INCLUDE listselectionhiddenfields %]
</p>
</form>
<p>
User is a member of any groups shown with a check or grey bar.
A grey bar indicates indirect membership, either derived from other
groups (marked with square brackets) or via regular expression
(marked with '*').
</p>
[% IF editusers %]
<p>
Square brackets around the bless checkbox indicate the ability
to bless users (grant them membership in the group) as a result
of membership in another group.
</p>
[% END %]
[% IF Param('allowuserdeletion') && editusers %]
<form method="post" action="editusers.cgi">
<p>
<input type="submit" value="Delete User" />
<input type="hidden" name="action" value="del" />
<input type="hidden" name="userid" value="[% otheruser.id %]" />
[% INCLUDE listselectionhiddenfields %]
</p>
</form>
[% END %]
<p>
You can also
[% IF editusers %]
<a href="editusers.cgi?action=add[% INCLUDE listselectionurlparams %]">add
a new user</a>
[% IF listselectionvalues %],
[% END %]
[% END %]
[% IF listselectionvalues %]
go
<a href="editusers.cgi?action=list[% INCLUDE listselectionurlparams %]">back
to the user list</a>,
[% END %]
[% IF editusers OR listselectionvalues %]
or
[% END %]
<a href="editusers.cgi">find other users</a>.
</p>
[% PROCESS global/footer.html.tmpl %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# listselectionvalues: selection values to recreate the current user list.
# editusers: is viewing user member of editusers?
# users: list of user information (id, login_name, realname,
# disabledtext).
#%]
[% PROCESS global/header.html.tmpl
title = "Select user"
style_urls = ['skins/standard/editusers.css']
%]
[% PROCESS admin/users/listselectvars.html.tmpl
listselectionvalues = listselectionvalues
%]
[% listselectionurlparams = INCLUDE listselectionurlparams %]
[% columns =
[{name => 'login_name'
heading => 'Edit user...'
contentlink => 'editusers.cgi?action=edit&amp;userid=%%userid%%' _
listselectionurlparams
allow_html_content => 1
}
{name => 'realname'
heading => 'Real name'
allow_html_content => 1
}
]
%]
[% IF Param('allowuserdeletion') && editusers %]
[% columns.push({heading => 'Action'
content => 'Delete'
contentlink => 'editusers.cgi?action=del' _
'&amp;userid=%%userid%%' _
listselectionurlparams
}
)
%]
[% END %]
[% FOREACH thisuser = users %]
[%# We FILTER html here because we need admin/table.html.tmpl to accept HTML
# for styling, so we cannot let admin/table.html.tmpl do the FILTER.
#%]
[% thisuser.login_name = BLOCK %]
[% thisuser.login_name FILTER html %]
[% END %]
[% IF thisuser.realname %]
[% thisuser.realname = BLOCK %]
[% thisuser.realname FILTER html %]
[% END %]
[% ELSE %]
[% SET thisuser.realname = '<span style="color: red">missing</span>' %]
[% END %]
[% IF thisuser.disabledtext %]
[% thisuser.login_name = "<span class=\"bz_inactive\">$thisuser.login_name</span>" %]
[% thisuser.realname = "<span class=\"bz_inactive\">$thisuser.realname</span>" %]
[% END %]
[% END %]
<p>[% users.size %] user[% "s" UNLESS users.size == 1 %] found.</p>
[% PROCESS admin/table.html.tmpl
columns = columns
data = users
%]
<p>
If you do not wish to modify a user account at this time, you can
<a href="editusers.cgi">find other users</a>
[% IF editusers %]
or
<a href="editusers.cgi?action=add[% INCLUDE listselectionurlparams %]">add
a new user</a>
[% END %].
</p>
[% PROCESS global/footer.html.tmpl %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# listselectionvalues: selection values to recreate the current user list.
#%]
[% BLOCK listselectionurlparams %]
[% FOREACH field = listselectionvalues.keys %]&amp;
[% field FILTER url_quote %]=
[% listselectionvalues.$field FILTER url_quote %]
[% END %]
[% END %]
[% BLOCK listselectionhiddenfields %]
[% FOREACH field = listselectionvalues.keys %]
<input type="hidden" name="[% field FILTER html %]"
value="[% listselectionvalues.$field FILTER html %]" />
[% END %]
[% END %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# editusers: is viewing user member of editusers?
# restrictablegroups: list of groups visible to the user:
# id: group id
# name: group name
#%]
[% PROCESS global/header.html.tmpl
title = "Search users"
style_urls = ['skins/standard/editusers.css']
onload = "document.forms['f'].matchstr.focus()"
%]
[% PROCESS admin/users/listselectvars.html.tmpl
listselectionvalues = listselectionvalues
%]
<form name="f" method="get" action="editusers.cgi">
<input type="hidden" name="action" value="list" />
<p><label for="matchstr">List users with login name matching</label>
<input size="32" name="matchstr" id="matchstr" />
<select name="matchtype">
<option value="substr" selected="selected">case-insensitive substring</option>
<option value="regexp">case-insensitive regexp</option>
<option value="notregexp">not (case-insensitive regexp)</option>
</select>
<input type="submit" value="Search" /></p>
[% IF restrictablegroups.size %]
<p><input type="checkbox" name="grouprestrict" value="1" id="grouprestrict" />
<label for="grouprestrict">Restrict to users belonging to group</label>
<select name="groupid"
onchange="document.forms['f'].grouprestrict.checked=true">
[% FOREACH group = restrictablegroups %]
<option value="[% group.id FILTER html %]">[% group.name FILTER html %]</option>
[% END %]
</select></p>
[% END %]
</form>
[% IF editusers %]
<p>
You can also <a href="editusers.cgi?action=add">add a new user</a>
[%- IF listselectionvalues %],
or
<a href="editusers.cgi?action=list[% INCLUDE listselectionurlparams %]">show
the user list again</a>
[%- END %].
</p>
[% END %]
[% PROCESS global/footer.html.tmpl %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
#%]
[%# INTERFACE:
#
# editform: is this an edit form? (It's a create form otherwise)
# editusers: is viewing user member of editusers?
# otheruser: Bugzilla::User object of user to edit
#%]
<tr>
<th><label for="login">Login name:</label></th>
<td>
[% IF editusers %]
<input size="64" maxlength="255" name="login"
id="login" value="[% otheruser.login FILTER html %]" />
[% IF editform %]
<input type="hidden" name="loginold"
value="[% otheruser.login FILTER html %]" />
[% END %]
[% ELSE %]
[% otheruser.login FILTER html %]
[% END %]
</td>
</tr>
<tr>
<th><label for="name">Real name:</label></th>
<td>
[% IF editusers %]
<input size="64" maxlength="255" name="name"
id="name" value="[% otheruser.name FILTER html %]" />
[% IF editform %]
<input type="hidden" name="nameold"
value="[% otheruser.name FILTER html %]" />
[% END %]
[% ELSE %]
[% otheruser.name FILTER html %]
[% END %]
</td>
</tr>
[% IF editusers %]
<tr>
<th><label for="password">Password:</label></th>
<td>
<input type="password" size="16" maxlength="16" name="password"
id="password" value="" />
[% IF editform %]<br />
(Enter new password to change.)
[% END %]
</td>
</tr>
<tr>
<th><label for="disabledtext">Disable text:</label></th>
<td>
<textarea name="disabledtext" rows="10"
id="disabledtext"
cols="60">[% otheruser.disabledtext FILTER html %]</textarea><br />
(If non-empty, then the account will be disabled, and this text should
explain why.)
[% IF editform %]
<input type="hidden" name="disabledtextold"
value="[% otheruser.disabledtext FILTER html %]" />
[% END %]
</td>
</tr>
[% END %]
......@@ -581,6 +581,31 @@
'deleted_bug_count'
],
'admin/users/confirm-delete.html.tmpl' => [
'andstring',
'responsibilityterms.$responsibility',
'bugs',
'cc',
'flags.requestee',
'flags.setter',
'longdescs',
'namedqueries',
'votes',
'series',
'watch.watched',
'watch.watcher',
'whine_events',
'whine_schedules',
'otheruser.id'
],
'admin/users/edit.html.tmpl' => [
'otheruser.id',
'group.id',
'perms.directbless',
'perms.directmember',
],
'admin/components/edit.html.tmpl' => [
'bug_count'
],
......
......@@ -185,7 +185,11 @@
[% ELSIF error == "invalid_keyword_id" %]
The keyword ID <em>[% id FILTER html %]</em> couldn't be
found.
[% ELSIF error == "invalid_user_id" %]
[% title = "Invalid User ID" %]
There is no user account with ID <em>[% userid FILTER html %]</em>.
[% ELSIF error == "missing_bug_id" %]
No [% terms.bug %] ID was given.
......
......@@ -30,7 +30,77 @@
[% message_tag = message %]
[% message = BLOCK %]
[% IF message_tag == "buglist_adding_field" %]
[% IF message_tag == "account_created" %]
[% title = "User $otheruser.login created" %]
A new user account [% otheruser.login FILTER html %] has been created
successfully.
[% IF groups.size %]
You may want to edit the group settings now, using the form below.
[% END %]
[% ELSIF message_tag == "account_updated" %]
[% IF changed_fields.size
+ groups_added_to.size + groups_removed_from.size
+ groups_granted_rights_to_bless.size + groups_denied_rights_to_bless.size %]
[% title = "User $loginold updated" %]
The following changes have been made to the user account
[%+ loginold FILTER html %]:
<ul>
[% FOREACH field = changed_fields %]
<li>
[% IF field == 'login_name' %]
The login is now [% otheruser.login FILTER html %].
[% ELSIF field == 'realname' %]
The real name has been updated.
[% ELSIF field == 'cryptpassword' %]
A new password has been set.
[% ELSIF field == 'disabledtext' %]
The disable text has been modified.
[% END %]
</li>
[% END %]
[% IF groups_added_to.size %]
<li>
The account has been added to the
[%+ groups_added_to.join(', ') FILTER html %]
group[% 's' IF groups_added_to.size > 1 %].
</li>
[% END %]
[% IF groups_removed_from.size %]
<li>
The account has been removed from the
[%+ groups_removed_from.join(', ') FILTER html %]
group[% 's' IF groups_removed_from.size > 1 %].
</li>
[% END %]
[% IF groups_granted_rights_to_bless.size %]
<li>
The account has been granted rights to bless the
[%+ groups_granted_rights_to_bless.join(', ') FILTER html %]
group[% 's' IF groups_granted_rights_to_bless.size > 1 %].
</li>
[% END %]
[% IF groups_denied_rights_to_bless.size %]
<li>
The account has been denied rights to bless the
[%+ groups_denied_rights_to_bless.join(', ') FILTER html %]
group[% 's' IF groups_denied_rights_to_bless.size > 1 %].
</li>
[% END %]
</ul>
[% ELSE %]
[% title = "User $otheruser.login not changed" %]
You didn't request any change on the user account
[%+ otheruser.login FILTER html %].
[% END %]
[%# changed_fields.join(', ') %]
[% ELSIF message_tag == "account_deleted" %]
[% title = "User $otheruser.login deleted" %]
The user account [% otheruser.login FILTER html %] has been deleted
successfully.
[% ELSIF message_tag == "buglist_adding_field" %]
[% title = "Adding field to search page..." %]
[% link = "Click here if the page does not redisplay automatically." %]
......
......@@ -108,6 +108,8 @@
[% IF group %] and [% END %]
[% IF reason == "cant_bless" %]
you don't have permissions to put people in or out of any group,
[% ELSIF reason == "not_visible" %]
there are visibility restrictions on certain user groups,
[% END %]
[% END %]
......@@ -146,6 +148,8 @@
products
[% ELSIF object == "reports" %]
whine reports
[% ELSIF object == "user" %]
the user you specified
[% ELSIF object == "users" %]
users
[% ELSIF object == "versions" %]
......@@ -1046,6 +1050,16 @@
[% title = "Deletion not activated" %]
Sorry, the deletion of user accounts is not allowed.
[% ELSIF error == "user_has_responsibility" %]
[% title = "Can't Delete User Account" %]
The user you want to delete is set up for roles as initial [% terms.bug %]
owner or QA contact for at least one component.
For this reason, you cannot delete the account at this time.
[% ELSIF error == "user_login_required" %]
[% title = "Login Name Required" %]
You must enter a login name for the new user.
[% ELSIF error == "votes_must_be_nonnegative" %]
[% title = "Votes Must Be Non-negative" %]
Only use non-negative numbers for your [% terms.bug %] votes.
......
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