Commit fb079a1b authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 347291: Make Bugzilla::User use Bugzilla::Object

Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=LpSolit, a=myk
parent d89140b0
...@@ -168,7 +168,7 @@ sub ProcessOneBug { ...@@ -168,7 +168,7 @@ sub ProcessOneBug {
# Convert to names, for later display # Convert to names, for later display
$values{'changer'} = $changer; $values{'changer'} = $changer;
$values{'changername'} = Bugzilla::User->new_from_login($changer)->name; $values{'changername'} = Bugzilla::User->new({name => $changer})->name;
$values{'assigned_to'} = user_id_to_login($values{'assigned_to'}); $values{'assigned_to'} = user_id_to_login($values{'assigned_to'});
$values{'reporter'} = user_id_to_login($values{'reporter'}); $values{'reporter'} = user_id_to_login($values{'reporter'});
if ($values{'qa_contact'}) { if ($values{'qa_contact'}) {
......
...@@ -224,6 +224,49 @@ sub bz_setup_database { ...@@ -224,6 +224,49 @@ sub bz_setup_database {
# login_name, which we do with sql_istrcmp all over the place. # login_name, which we do with sql_istrcmp all over the place.
$self->bz_add_index('profiles', 'profiles_login_name_lower_idx', $self->bz_add_index('profiles', 'profiles_login_name_lower_idx',
{FIELDS => ['LOWER(login_name)'], TYPE => 'UNIQUE'}); {FIELDS => ['LOWER(login_name)'], TYPE => 'UNIQUE'});
# Now that Bugzilla::Object uses sql_istrcmp, other tables
# also need a LOWER() index.
_fix_case_differences('fielddefs', 'name');
$self->bz_add_index('fielddefs', 'fielddefs_name_lower_idx',
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
_fix_case_differences('keyworddefs', 'name');
$self->bz_add_index('keyworddefs', 'keyworddefs_name_lower_idx',
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
_fix_case_differences('products', 'name');
$self->bz_add_index('products', 'products_name_lower_idx',
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
}
# Renames things that differ only in case.
sub _fix_case_differences {
my ($table, $field) = @_;
my $dbh = Bugzilla->dbh;
my $duplicates = $dbh->selectcol_arrayref(
"SELECT DISTINCT LOWER($field) FROM $table
GROUP BY LOWER($field) HAVING COUNT(LOWER($field)) > 1");
foreach my $name (@$duplicates) {
my $dups = $dbh->selectcol_arrayref(
"SELECT $field FROM $table WHERE LOWER($field) = ?",
undef, $name);
my $primary = shift @$dups;
foreach my $dup (@$dups) {
my $new_name = "${dup}_";
# Make sure the new name isn't *also* a duplicate.
while (1) {
last if (!$dbh->selectrow_array(
"SELECT 1 FROM $table WHERE LOWER($field) = ?",
undef, lc($new_name)));
$new_name .= "_";
}
print "$table '$primary' and '$dup' have names that differ",
" only in case.\nRenaming '$dup' to '$new_name'...\n";
$dbh->do("UPDATE $table SET $field = ? WHERE $field = ?",
undef, $new_name, $dup);
}
}
} }
##################################################################### #####################################################################
......
...@@ -330,7 +330,7 @@ sub validate { ...@@ -330,7 +330,7 @@ sub validate {
# We know the requestee exists because we ran # We know the requestee exists because we ran
# Bugzilla::User::match_field before getting here. # Bugzilla::User::match_field before getting here.
my $requestee = Bugzilla::User->new_from_login($login); my $requestee = new Bugzilla::User({ name => $login });
# Throw an error if the user can't see the bug. # Throw an error if the user can't see the bug.
# Note that if permissions on this bug are changed, # Note that if permissions on this bug are changed,
...@@ -593,7 +593,7 @@ sub modify { ...@@ -593,7 +593,7 @@ sub modify {
create({ type => $flag->type, create({ type => $flag->type,
setter => $setter, setter => $setter,
status => "?", status => "?",
requestee => Bugzilla::User->new_from_login($login) }, requestee => new Bugzilla::User({ name => $login }) },
$bug, $attachment, $timestamp); $bug, $attachment, $timestamp);
} }
} }
...@@ -796,7 +796,8 @@ sub FormToNewFlags { ...@@ -796,7 +796,8 @@ sub FormToNewFlags {
push (@flags, { type => $flag_type , push (@flags, { type => $flag_type ,
setter => $setter , setter => $setter ,
status => $status , status => $status ,
requestee => Bugzilla::User->new_from_login($login) }); requestee =>
new Bugzilla::User({ name => $login }) });
last unless $flag_type->is_multiplicable; last unless $flag_type->is_multiplicable;
} }
} }
...@@ -843,7 +844,7 @@ sub notify { ...@@ -843,7 +844,7 @@ sub notify {
if (scalar(@bug_in_groups) || $attachment_is_private) { if (scalar(@bug_in_groups) || $attachment_is_private) {
my @new_cc_list; my @new_cc_list;
foreach my $cc (split(/[, ]+/, $flag->type->cc_list)) { foreach my $cc (split(/[, ]+/, $flag->type->cc_list)) {
my $ccuser = Bugzilla::User->new_from_login($cc) || next; my $ccuser = new Bugzilla::User({ name => $cc }) || next;
next if (scalar(@bug_in_groups) && !$ccuser->can_see_bug($bug->bug_id)); next if (scalar(@bug_in_groups) && !$ccuser->can_see_bug($bug->bug_id));
next if $attachment_is_private next if $attachment_is_private
......
...@@ -451,7 +451,7 @@ sub validate { ...@@ -451,7 +451,7 @@ sub validate {
foreach my $login (@requestees) { foreach my $login (@requestees) {
# We know the requestee exists because we ran # We know the requestee exists because we ran
# Bugzilla::User::match_field before getting here. # Bugzilla::User::match_field before getting here.
my $requestee = Bugzilla::User->new_from_login($login); my $requestee = new Bugzilla::User({ name => $login });
# Throw an error if the user can't see the bug. # Throw an error if the user can't see the bug.
if (!$requestee->can_see_bug($bug_id)) { if (!$requestee->can_see_bug($bug_id)) {
......
...@@ -21,7 +21,9 @@ package Bugzilla::Object; ...@@ -21,7 +21,9 @@ package Bugzilla::Object;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use constant LIST_ORDER => 'name'; use constant NAME_FIELD => 'name';
use constant ID_FIELD => 'id';
use constant LIST_ORDER => NAME_FIELD;
############################### ###############################
#### Initialization #### #### Initialization ####
...@@ -35,12 +37,19 @@ sub new { ...@@ -35,12 +37,19 @@ sub new {
return $object; return $object;
} }
# Note: Because this uses sql_istrcmp, if you make a new object use
# Bugzilla::Object, make sure that you modify bz_setup_database
# in Bugzilla::DB::Pg appropriately, to add the right LOWER
# index. You can see examples already there.
sub _init { sub _init {
my $class = shift; my $class = shift;
my ($param) = @_; my ($param) = @_;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $columns = join(',', $class->DB_COLUMNS); my $columns = join(',', $class->DB_COLUMNS);
my $table = $class->DB_TABLE; my $table = $class->DB_TABLE;
my $name_field = $class->NAME_FIELD;
my $id_field = $class->ID_FIELD;
my $id = $param unless (ref $param eq 'HASH'); my $id = $param unless (ref $param eq 'HASH');
my $object; my $object;
...@@ -52,12 +61,13 @@ sub _init { ...@@ -52,12 +61,13 @@ sub _init {
$object = $dbh->selectrow_hashref(qq{ $object = $dbh->selectrow_hashref(qq{
SELECT $columns FROM $table SELECT $columns FROM $table
WHERE id = ?}, undef, $id); WHERE $id_field = ?}, undef, $id);
} elsif (defined $param->{'name'}) { } elsif (defined $param->{'name'}) {
trick_taint($param->{'name'}); trick_taint($param->{'name'});
$object = $dbh->selectrow_hashref(qq{ $object = $dbh->selectrow_hashref(qq{
SELECT $columns FROM $table SELECT $columns FROM $table
WHERE name = ?}, undef, $param->{'name'}); WHERE } . $dbh->sql_istrcmp($name_field, '?'),
undef, $param->{'name'});
} else { } else {
ThrowCodeError('bad_arg', ThrowCodeError('bad_arg',
{argument => 'param', {argument => 'param',
...@@ -74,6 +84,7 @@ sub new_from_list { ...@@ -74,6 +84,7 @@ sub new_from_list {
my $columns = join(',', $class->DB_COLUMNS); my $columns = join(',', $class->DB_COLUMNS);
my $table = $class->DB_TABLE; my $table = $class->DB_TABLE;
my $order = $class->LIST_ORDER; my $order = $class->LIST_ORDER;
my $id_field = $class->ID_FIELD;
my $objects; my $objects;
if (@$id_list) { if (@$id_list) {
...@@ -85,7 +96,7 @@ sub new_from_list { ...@@ -85,7 +96,7 @@ sub new_from_list {
push(@detainted_ids, $id); push(@detainted_ids, $id);
} }
$objects = $dbh->selectall_arrayref( $objects = $dbh->selectall_arrayref(
"SELECT $columns FROM $table WHERE id IN (" "SELECT $columns FROM $table WHERE $id_field IN ("
. join(',', @detainted_ids) . ") ORDER BY $order", {Slice=>{}}); . join(',', @detainted_ids) . ") ORDER BY $order", {Slice=>{}});
} else { } else {
return []; return [];
...@@ -152,9 +163,10 @@ sub get_all { ...@@ -152,9 +163,10 @@ sub get_all {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $table = $class->DB_TABLE; my $table = $class->DB_TABLE;
my $order = $class->LIST_ORDER; my $order = $class->LIST_ORDER;
my $id_field = $class->ID_FIELD;
my $ids = $dbh->selectcol_arrayref(qq{ my $ids = $dbh->selectcol_arrayref(qq{
SELECT id FROM $table ORDER BY $order}); SELECT $id_field FROM $table ORDER BY $order});
my $objects = $class->new_from_list($ids); my $objects = $class->new_from_list($ids);
return @$objects; return @$objects;
...@@ -206,11 +218,24 @@ for C<Bugzilla::Keyword> this would be C<keyworddefs>. ...@@ -206,11 +218,24 @@ for C<Bugzilla::Keyword> this would be C<keyworddefs>.
The names of the columns that you want to read out of the database The names of the columns that you want to read out of the database
and into this object. This should be an array. and into this object. This should be an array.
=item C<NAME_FIELD>
The name of the column that should be considered to be the unique
"name" of this object. The 'name' is a B<string> that uniquely identifies
this Object in the database. Defaults to 'name'. When you specify
C<{name => $name}> to C<new()>, this is the column that will be
matched against in the DB.
=item C<ID_FIELD>
The name of the column that represents the unique B<integer> ID
of this object in the database. Defaults to 'id'.
=item C<LIST_ORDER> =item C<LIST_ORDER>
The order that C<new_from_list> and C<get_all> should return objects The order that C<new_from_list> and C<get_all> should return objects
in. This should be the name of a database column. Defaults to in. This should be the name of a database column. Defaults to
C<name>. L</NAME_FIELD>.
=item C<REQUIRED_CREATE_FIELDS> =item C<REQUIRED_CREATE_FIELDS>
...@@ -240,7 +265,8 @@ They must return the validated value. ...@@ -240,7 +265,8 @@ They must return the validated value.
id of the object, from the database, that we id of the object, from the database, that we
want to read in. If you pass in a hash with want to read in. If you pass in a hash with
C<name> key, then the value of the name key C<name> key, then the value of the name key
is the name of the object from the DB. is the case-insensitive name of the object from
the DB.
Returns: A fully-initialized object. Returns: A fully-initialized object.
......
...@@ -48,7 +48,7 @@ use Bugzilla::Product; ...@@ -48,7 +48,7 @@ use Bugzilla::Product;
use Bugzilla::Classification; use Bugzilla::Classification;
use Bugzilla::Field; use Bugzilla::Field;
use base qw(Exporter); use base qw(Bugzilla::Object Exporter);
@Bugzilla::User::EXPORT = qw(insert_new_user is_available_username @Bugzilla::User::EXPORT = qw(insert_new_user is_available_username
login_to_id user_id_to_login validate_password login_to_id user_id_to_login validate_password
UserInGroup UserInGroup
...@@ -66,95 +66,51 @@ use constant USER_MATCH_SUCCESS => 1; ...@@ -66,95 +66,51 @@ use constant USER_MATCH_SUCCESS => 1;
use constant MATCH_SKIP_CONFIRM => 1; use constant MATCH_SKIP_CONFIRM => 1;
use constant DEFAULT_USER => {
'id' => 0,
'name' => '',
'login' => '',
'showmybugslink' => 0,
'disabledtext' => '',
'disable_mail' => 0,
};
use constant DB_TABLE => 'profiles';
# XXX Note that Bugzilla::User->name does not return the same thing
# that you passed in for "name" to new(). That's because historically
# Bugzilla::User used "name" for the realname field. This should be
# fixed one day.
use constant DB_COLUMNS => (
'profiles.userid AS id',
'profiles.login_name AS login',
'profiles.realname AS name',
'profiles.mybugslink AS showmybugslink',
'profiles.disabledtext',
'profiles.disable_mail',
);
use constant NAME_FIELD => 'login_name';
use constant ID_FIELD => 'userid';
################################################################################ ################################################################################
# Functions # Functions
################################################################################ ################################################################################
sub new { sub new {
my $invocant = shift; my $invocant = shift;
my $user_id = shift;
if ($user_id) {
my $uid = $user_id;
detaint_natural($user_id)
|| ThrowCodeError('invalid_numeric_argument',
{argument => 'userID',
value => $uid,
function => 'Bugzilla::User::new'});
return $invocant->_create("userid=?", $user_id);
}
else {
return $invocant->_create;
}
}
# This routine is sort of evil. Nothing except the login stuff should
# be dealing with addresses as an input, and they can get the id as a
# side effect of the other sql they have to do anyway.
# Bugzilla::BugMail still does this, probably as a left over from the
# pre-id days. Provide this as a helper, but don't document it, and hope
# that it can go away.
# The request flag stuff also does this, but it really should be passing
# in the id it already had to validate (or the User.pm object, of course)
sub new_from_login {
my $invocant = shift;
my $login = shift;
my $dbh = Bugzilla->dbh;
return $invocant->_create($dbh->sql_istrcmp('login_name', '?'), $login);
}
# Internal helper for the above |new| methods
# $cond is a string (including a placeholder ?) for the search
# requirement for the profiles table
sub _create {
my $invocant = shift;
my $class = ref($invocant) || $invocant; my $class = ref($invocant) || $invocant;
my ($param) = @_;
my $cond = shift; my $user = DEFAULT_USER;
my $val = shift; bless ($user, $class);
return $user unless $param;
# Allow invocation with no parameters to create a blank object
my $self = {
'id' => 0,
'name' => '',
'login' => '',
'showmybugslink' => 0,
'disabledtext' => '',
'flags' => {},
'disable_mail' => 0,
};
bless ($self, $class);
return $self unless $cond && $val;
# We're checking for validity here, so any value is OK
trick_taint($val);
my $dbh = Bugzilla->dbh; return $class->SUPER::new(@_);
my ($id, $login, $name, $disabledtext, $mybugslink, $disable_mail) =
$dbh->selectrow_array(qq{SELECT userid, login_name, realname,
disabledtext, mybugslink, disable_mail
FROM profiles WHERE $cond},
undef, $val);
return undef unless defined $id;
$self->{'id'} = $id;
$self->{'name'} = $name;
$self->{'login'} = $login;
$self->{'disabledtext'} = $disabledtext;
$self->{'showmybugslink'} = $mybugslink;
$self->{'disable_mail'} = $disable_mail;
return $self;
} }
# Accessors for user attributes # Accessors for user attributes
sub id { $_[0]->{id}; }
sub login { $_[0]->{login}; } sub login { $_[0]->{login}; }
sub email { $_[0]->{login} . Bugzilla->params->{'emailsuffix'}; } sub email { $_[0]->{login} . Bugzilla->params->{'emailsuffix'}; }
sub name { $_[0]->{name}; }
sub disabledtext { $_[0]->{'disabledtext'}; } sub disabledtext { $_[0]->{'disabledtext'}; }
sub is_disabled { $_[0]->disabledtext ? 1 : 0; } sub is_disabled { $_[0]->disabledtext ? 1 : 0; }
sub showmybugslink { $_[0]->{showmybugslink}; } sub showmybugslink { $_[0]->{showmybugslink}; }
...@@ -844,7 +800,7 @@ sub match { ...@@ -844,7 +800,7 @@ sub match {
# User objects. # User objects.
my $user_logins = $dbh->selectcol_arrayref($query, undef, ($wildstr, $wildstr)); my $user_logins = $dbh->selectcol_arrayref($query, undef, ($wildstr, $wildstr));
foreach my $login_name (@$user_logins) { foreach my $login_name (@$user_logins) {
push(@users, Bugzilla::User->new_from_login($login_name)); push(@users, new Bugzilla::User({ name => $login_name }));
} }
} }
else { # try an exact match else { # try an exact match
...@@ -884,7 +840,7 @@ sub match { ...@@ -884,7 +840,7 @@ sub match {
my $user_logins = $dbh->selectcol_arrayref($query, undef, ($str, $str)); my $user_logins = $dbh->selectcol_arrayref($query, undef, ($str, $str));
foreach my $login_name (@$user_logins) { foreach my $login_name (@$user_logins) {
push(@users, Bugzilla::User->new_from_login($login_name)); push(@users, new Bugzilla::User({ name => $login_name }));
} }
} }
return \@users; return \@users;
...@@ -1513,6 +1469,10 @@ there is currently no way to modify a user from this package. ...@@ -1513,6 +1469,10 @@ there is currently no way to modify a user from this package.
Note that the currently logged in user (if any) is available via Note that the currently logged in user (if any) is available via
L<Bugzilla-E<gt>user|Bugzilla/"user">. L<Bugzilla-E<gt>user|Bugzilla/"user">.
C<Bugzilla::User> is an implementation of L<Bugzilla::Object>, and thus
provides all the methods of L<Bugzilla::Object> in addition to the
methods listed below.
=head1 CONSTANTS =head1 CONSTANTS
=over =over
...@@ -1541,26 +1501,7 @@ confirmation screen. ...@@ -1541,26 +1501,7 @@ confirmation screen.
=head1 METHODS =head1 METHODS
=over 4 =over
=item C<new($userid)>
Creates a new C<Bugzilla::User> object for the given user id. If no user
id was given, a blank object is created with no user attributes.
If an id was given but there was no matching user found, undef is returned.
=begin undocumented
=item C<new_from_login($login)>
Creates a new C<Bugzilla::User> object given the provided login. Returns
C<undef> if no matching user is found.
This routine should not be required in general; most scripts should be using
userids instead.
=end undocumented
=item C<id> =item C<id>
......
...@@ -797,7 +797,7 @@ sub check_user { ...@@ -797,7 +797,7 @@ sub check_user {
$vars->{'user_id'} = $otherUserID; $vars->{'user_id'} = $otherUserID;
} }
elsif ($otherUserLogin) { elsif ($otherUserLogin) {
$otherUser = Bugzilla::User->new_from_login($otherUserLogin); $otherUser = new Bugzilla::User({ name => $otherUserLogin });
$vars->{'user_login'} = $otherUserLogin; $vars->{'user_login'} = $otherUserLogin;
} }
($otherUser && $otherUser->id) || ThrowCodeError('invalid_user', $vars); ($otherUser && $otherUser->id) || ThrowCodeError('invalid_user', $vars);
......
...@@ -179,7 +179,7 @@ sub flag_handler { ...@@ -179,7 +179,7 @@ sub flag_handler {
my $type = ($attachid) ? "attachment" : "bug"; my $type = ($attachid) ? "attachment" : "bug";
my $err = ''; my $err = '';
my $setter = Bugzilla::User->new_from_login($setter_login); my $setter = new Bugzilla::User({ name => $setter_login });
my $requestee; my $requestee;
my $requestee_id; my $requestee_id;
...@@ -195,7 +195,7 @@ sub flag_handler { ...@@ -195,7 +195,7 @@ sub flag_handler {
} }
my $setter_id = $setter->id; my $setter_id = $setter->id;
if ( defined($requestee_login) ) { if ( defined($requestee_login) ) {
$requestee = Bugzilla::User->new_from_login($requestee_login); $requestee = new Bugzilla::User({ name => $requestee_login });
if ( $requestee ) { if ( $requestee ) {
if ( !$requestee->can_see_bug($bugid) ) { if ( !$requestee->can_see_bug($bugid) ) {
$err .= "Requestee is not a member of bug group\n"; $err .= "Requestee is not a member of bug group\n";
...@@ -423,7 +423,7 @@ sub process_bug { ...@@ -423,7 +423,7 @@ sub process_bug {
my $root = $twig->root; my $root = $twig->root;
my $maintainer = $root->{'att'}->{'maintainer'}; my $maintainer = $root->{'att'}->{'maintainer'};
my $exporter_login = $root->{'att'}->{'exporter'}; my $exporter_login = $root->{'att'}->{'exporter'};
my $exporter = Bugzilla::User->new_from_login($exporter_login); my $exporter = new Bugzilla::User({ name => $exporter_login });
my $urlbase = $root->{'att'}->{'urlbase'}; my $urlbase = $root->{'att'}->{'urlbase'};
# We will store output information in this variable. # We will store output information in this variable.
......
...@@ -125,7 +125,7 @@ elsif ($action eq 'begin-sudo') { ...@@ -125,7 +125,7 @@ elsif ($action eq 'begin-sudo') {
# Get & verify the target user (the user who we will be impersonating) # Get & verify the target user (the user who we will be impersonating)
my $target_user = my $target_user =
Bugzilla::User->new_from_login($cgi->param('target_login')); new Bugzilla::User({ name => $cgi->param('target_login') });
unless (defined($target_user) unless (defined($target_user)
&& $target_user->id && $target_user->id
&& $user->can_see_user($target_user)) && $user->can_see_user($target_user))
......
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