RSS

(root)/bugzilla/trunk : /editusers.cgi (revision 8628)

To get this branch, use:
bzr branch /bugzilla/trunk
Line Revision Contents
1 3995
#!/usr/bin/perl -wT
2 8075
# This Source Code Form is subject to the terms of the Mozilla Public
3
# License, v. 2.0. If a copy of the MPL was not distributed with this
4
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
#
6
# This Source Code Form is "Incompatible With Secondary Licenses", as
7
# defined by the Mozilla Public License, v. 2.0.
8 353
9 8378
use 5.10.1;
10 353
use strict;
11 5304
use lib qw(. lib);
12 353
13 3192
use Bugzilla;
14 4296
use Bugzilla::Constants;
15
use Bugzilla::Util;
16
use Bugzilla::Error;
17 2178
use Bugzilla::User;
18 3632
use Bugzilla::Bug;
19 4576
use Bugzilla::BugMail;
20 3632
use Bugzilla::Flag;
21 3667
use Bugzilla::Field;
22 3843
use Bugzilla::Group;
23 4612
use Bugzilla::Token;
24 8564
use Bugzilla::Mailer;
25 2178
26 3843
my $user = Bugzilla->login(LOGIN_REQUIRED);
27 353
28 3192
my $cgi       = Bugzilla->cgi;
29
my $template  = Bugzilla->template;
30 3168
my $dbh       = Bugzilla->dbh;
31 3192
my $userid    = $user->id;
32
my $editusers = $user->in_group('editusers');
33 4329
local our $vars     = {};
34 3168
35
# Reject access if there is no sense in continuing.
36
$editusers
37 3684
    || $user->can_bless()
38 3168
    || ThrowUserError("auth_failure", {group  => "editusers",
39
                                       reason => "cant_bless",
40
                                       action => "edit",
41
                                       object => "users"});
42
43 3843
print $cgi->header();
44 353
45 3192
# Common CGI params
46 3843
my $action         = $cgi->param('action') || 'search';
47
my $otherUserID    = $cgi->param('userid');
48
my $otherUserLogin = $cgi->param('user');
49 4612
my $token          = $cgi->param('token');
50 3192
51
# Prefill template vars with data used in all or nearly all templates
52 3168
$vars->{'editusers'} = $editusers;
53
mirrorListSelectionValues();
54
55 8151
Bugzilla::Hook::process('admin_editusers_action',
56
    { vars => $vars, user => $user, action => $action });
57
58 3168
###########################################################################
59
if ($action eq 'search') {
60
    # Allow to restrict the search to any group the user is allowed to bless.
61 3596
    $vars->{'restrictablegroups'} = $user->bless_groups();
62 3168
    $template->process('admin/users/search.html.tmpl', $vars)
63
       || ThrowTemplateError($template->error());
64
65
###########################################################################
66
} elsif ($action eq 'list') {
67 3987
    my $matchvalue    = $cgi->param('matchvalue') || '';
68 7944
    my $matchstr      = trim($cgi->param('matchstr'));
69 3168
    my $matchtype     = $cgi->param('matchtype');
70
    my $grouprestrict = $cgi->param('grouprestrict') || '0';
71 7970
    my $enabled_only  = $cgi->param('enabled_only') || '0';
72 8088
    my $query = 'SELECT DISTINCT userid, login_name, realname, is_enabled, ' .
73
                $dbh->sql_date_format('last_seen_date', '%Y-%m-%d') . ' AS last_seen_date ' .
74 3168
                'FROM profiles';
75
    my @bindValues;
76
    my $nextCondition;
77 3354
    my $visibleGroups;
78 3168
79 3843
    # If a group ID is given, make sure it is a valid one.
80
    my $group;
81
    if ($grouprestrict) {
82
        $group = new Bugzilla::Group(scalar $cgi->param('groupid'));
83
        $group || ThrowUserError('invalid_group_ID');
84
    }
85
86 4317
    if (!$editusers && Bugzilla->params->{'usevisibilitygroups'}) {
87 3168
        # Show only users in visible groups.
88 3539
        $visibleGroups = $user->visible_groups_as_string();
89 3354
90
        if ($visibleGroups) {
91
            $query .= qq{, user_group_map AS ugm
92
                         WHERE ugm.user_id = profiles.userid
93
                           AND ugm.isbless = 0
94
                           AND ugm.group_id IN ($visibleGroups)
95
                        };
96
            $nextCondition = 'AND';
97
        }
98 3168
    } else {
99 3354
        $visibleGroups = 1;
100 3168
        if ($grouprestrict eq '1') {
101 3915
            $query .= qq{, user_group_map AS ugm
102
                         WHERE ugm.user_id = profiles.userid
103
                           AND ugm.isbless = 0
104
                        };
105
            $nextCondition = 'AND';
106
        }
107
        else {
108
            $nextCondition = 'WHERE';
109
        }
110 3168
    }
111
112 3354
    if (!$visibleGroups) {
113
        $vars->{'users'} = {};
114
    }
115
    else {
116 3987
        # Handle selection by login name, real name, or userid.
117 3354
        if (defined($matchtype)) {
118 3653
            $query .= " $nextCondition ";
119 3987
            my $expr = "";
120
            if ($matchvalue eq 'userid') {
121
                if ($matchstr) {
122
                    my $stored_matchstr = $matchstr;
123
                    detaint_natural($matchstr) 
124
                        || ThrowUserError('illegal_user_id', {userid => $stored_matchstr});
125
                }
126
                $expr = "profiles.userid";
127
            } elsif ($matchvalue eq 'realname') {
128
                $expr = "profiles.realname";
129
            } else {
130
                $expr = "profiles.login_name";
131
            }
132 6336
133 7942
            if ($matchtype =~ /^(regexp|notregexp|exact)$/) {
134 6336
                $matchstr ||= '.';
135
            }
136
            else {
137
                $matchstr = '' unless defined $matchstr;
138
            }
139
            # We can trick_taint because we use the value in a SELECT only,
140
            # using a placeholder.
141
            trick_taint($matchstr);
142
143 3354
            if ($matchtype eq 'regexp') {
144 6336
                $query .= $dbh->sql_regexp($expr, '?', 0, $dbh->quote($matchstr));
145 3354
            } elsif ($matchtype eq 'notregexp') {
146 6336
                $query .= $dbh->sql_not_regexp($expr, '?', 0, $dbh->quote($matchstr));
147 3665
            } elsif ($matchtype eq 'exact') {
148
                $query .= $expr . ' = ?';
149 3354
            } else { # substr or unknown
150 8435
                $query .= $dbh->sql_iposition('?', $expr) . ' > 0';
151 3354
            }
152
            $nextCondition = 'AND';
153
            push(@bindValues, $matchstr);
154
        }
155
156
        # Handle selection by group.
157
        if ($grouprestrict eq '1') {
158 3619
            my $grouplist = join(',',
159 6430
                @{Bugzilla::Group->flatten_group_membership($group->id)});
160 3915
            $query .= " $nextCondition ugm.group_id IN($grouplist) ";
161 3354
        }
162 7970
163
        if ($enabled_only eq '1') {
164
            $query .= " $nextCondition profiles.is_enabled = 1 ";
165
            $nextCondition = 'AND';
166
        }
167
168 3354
        $query .= ' ORDER BY profiles.login_name';
169
170
        $vars->{'users'} = $dbh->selectall_arrayref($query,
171
                                                    {'Slice' => {}},
172
                                                    @bindValues);
173 3665
174
    }
175
176 6316
    if ($matchtype && $matchtype eq 'exact' && scalar(@{$vars->{'users'}}) == 1) {
177 3843
        my $match_user_id = $vars->{'users'}[0]->{'userid'};
178
        my $match_user = check_user($match_user_id);
179
        edit_processing($match_user);
180 3665
    } else {
181
        $template->process('admin/users/list.html.tmpl', $vars)
182
            || ThrowTemplateError($template->error());
183
    }
184 3168
185
###########################################################################
186
} elsif ($action eq 'add') {
187
    $editusers || ThrowUserError("auth_failure", {group  => "editusers",
188
                                                  action => "add",
189
                                                  object => "users"});
190
191 4612
    $vars->{'token'} = issue_session_token('add_user');
192
193 3168
    $template->process('admin/users/create.html.tmpl', $vars)
194
       || ThrowTemplateError($template->error());
195
196
###########################################################################
197
} elsif ($action eq 'new') {
198
    $editusers || ThrowUserError("auth_failure", {group  => "editusers",
199
                                                  action => "add",
200
                                                  object => "users"});
201
202 4612
    check_token_data($token, 'add_user');
203
204 6918
    # When e.g. the 'Env' auth method is used, the password field
205
    # is not displayed. In that case, set the password to *.
206
    my $password = $cgi->param('password');
207
    $password = '*' if !defined $password;
208
209 4508
    my $new_user = Bugzilla::User->create({
210 4517
        login_name    => scalar $cgi->param('login'),
211 6918
        cryptpassword => $password,
212 4517
        realname      => scalar $cgi->param('name'),
213
        disabledtext  => scalar $cgi->param('disabledtext'),
214 7796
        disable_mail  => scalar $cgi->param('disable_mail'),
215
        extern_id     => scalar $cgi->param('extern_id'),
216
        });
217 4508
218
    userDataToVars($new_user->id);
219 3168
220 4612
    delete_token($token);
221
222 8564
    if ($cgi->param('notify_user')) {
223
        $vars->{'new_user'} = $new_user;
224
        my $message;
225
      
226
        $template->process('email/new-user-details.txt.tmpl', $vars, \$message)
227
            || ThrowTemplateError($template->error());
228
        MessageToMTA($message);
229
    }
230
231 4612
    # We already display the updated page. We have to recreate a token now.
232
    $vars->{'token'} = issue_session_token('edit_user');
233 3168
    $vars->{'message'} = 'account_created';
234
    $template->process('admin/users/edit.html.tmpl', $vars)
235
       || ThrowTemplateError($template->error());
236
237
###########################################################################
238
} elsif ($action eq 'edit') {
239 3843
    my $otherUser = check_user($otherUserID, $otherUserLogin);
240
    edit_processing($otherUser);
241 3168
242
###########################################################################
243
} elsif ($action eq 'update') {
244 4612
    check_token_data($token, 'edit_user');
245 3843
    my $otherUser = check_user($otherUserID, $otherUserLogin);
246
    $otherUserID = $otherUser->id;
247
248 3168
    # Lock tables during the check+update session.
249 5289
    $dbh->bz_start_transaction();
250 3168
 
251 3695
    $editusers || $user->can_see_user($otherUser)
252 3168
        || ThrowUserError('auth_failure', {reason => "not_visible",
253
                                           action => "modify",
254 3180
                                           object => "user"});
255 3168
256 4633
    $vars->{'loginold'} = $otherUser->login;
257 3843
258 3168
    # Update profiles table entry; silently skip doing this if the user
259
    # is not authorized.
260 6256
    my $changes = {};
261 3168
    if ($editusers) {
262 4633
        $otherUser->set_login($cgi->param('login'));
263
        $otherUser->set_name($cgi->param('name'));
264
        $otherUser->set_password($cgi->param('password'))
265
            if $cgi->param('password');
266
        $otherUser->set_disabledtext($cgi->param('disabledtext'));
267
        $otherUser->set_disable_mail($cgi->param('disable_mail'));
268 7796
        $otherUser->set_extern_id($cgi->param('extern_id'))
269
            if defined($cgi->param('extern_id'));
270 6256
        $changes = $otherUser->update();
271 3168
    }
272
273
    # Update group settings.
274
    my $sth_add_mapping = $dbh->prepare(
275
        qq{INSERT INTO user_group_map (
276
                  user_id, group_id, isbless, grant_type
277
                 ) VALUES (
278
                  ?, ?, ?, ?
279
                 )
280
          });
281
    my $sth_remove_mapping = $dbh->prepare(
282
        qq{DELETE FROM user_group_map
283
            WHERE user_id = ?
284
              AND group_id = ?
285
              AND isbless = ?
286
              AND grant_type = ?
287
          });
288
289
    my @groupsAddedTo;
290
    my @groupsRemovedFrom;
291
    my @groupsGrantedRightsToBless;
292
    my @groupsDeniedRightsToBless;
293
294
    # Regard only groups the user is allowed to bless and skip all others
295
    # silently.
296 3192
    # XXX: checking for existence of each user_group_map entry
297
    #      would allow to display a friendlier error message on page reloads.
298 4978
    userDataToVars($otherUserID);
299
    my $permissions = $vars->{'permissions'};
300 6177
    foreach my $blessable (@{$user->bless_groups()}) {
301
        my $id = $blessable->id;
302
        my $name = $blessable->name;
303 3168
304
        # Change memberships.
305 4978
        my $groupid = $cgi->param("group_$id") || 0;
306
        if ($groupid != $permissions->{$id}->{'directmember'}) {
307
            if (!$groupid) {
308 3168
                $sth_remove_mapping->execute(
309
                    $otherUserID, $id, 0, GRANT_DIRECT);
310 3337
                push(@groupsRemovedFrom, $name);
311 3168
            } else {
312
                $sth_add_mapping->execute(
313
                    $otherUserID, $id, 0, GRANT_DIRECT);
314 3337
                push(@groupsAddedTo, $name);
315 3168
            }
316
        }
317
318
        # Only members of the editusers group may change bless grants.
319
        # Skip silently if this is not the case.
320
        if ($editusers) {
321 4978
            my $groupid = $cgi->param("bless_$id") || 0;
322
            if ($groupid != $permissions->{$id}->{'directbless'}) {
323
                if (!$groupid) {
324 3168
                    $sth_remove_mapping->execute(
325
                        $otherUserID, $id, 1, GRANT_DIRECT);
326 3337
                    push(@groupsDeniedRightsToBless, $name);
327 3168
                } else {
328
                    $sth_add_mapping->execute(
329
                        $otherUserID, $id, 1, GRANT_DIRECT);
330 3337
                    push(@groupsGrantedRightsToBless, $name);
331 3168
                }
332
            }
333
        }
334
    }
335
    if (@groupsAddedTo || @groupsRemovedFrom) {
336
        $dbh->do(qq{INSERT INTO profiles_activity (
337
                           userid, who,
338
                           profiles_when, fieldid,
339
                           oldvalue, newvalue
340
                          ) VALUES (
341
                           ?, ?, now(), ?, ?, ?
342
                          )
343
                   },
344
                 undef,
345
                 ($otherUserID, $userid,
346 3667
                  get_field_id('bug_group'),
347 3168
                  join(', ', @groupsRemovedFrom), join(', ', @groupsAddedTo)));
348
    }
349 3192
    # XXX: should create profiles_activity entries for blesser changes.
350 3168
351 5289
    $dbh->bz_commit_transaction();
352 3168
353 3192
    # XXX: userDataToVars may be off when editing ourselves.
354 3168
    userDataToVars($otherUserID);
355 4612
    delete_token($token);
356 3168
357
    $vars->{'message'} = 'account_updated';
358 6256
    $vars->{'changed_fields'} = [keys %$changes];
359 3168
    $vars->{'groups_added_to'} = \@groupsAddedTo;
360
    $vars->{'groups_removed_from'} = \@groupsRemovedFrom;
361
    $vars->{'groups_granted_rights_to_bless'} = \@groupsGrantedRightsToBless;
362
    $vars->{'groups_denied_rights_to_bless'} = \@groupsDeniedRightsToBless;
363 4612
    # We already display the updated page. We have to recreate a token now.
364
    $vars->{'token'} = issue_session_token('edit_user');
365
366 3168
    $template->process('admin/users/edit.html.tmpl', $vars)
367
       || ThrowTemplateError($template->error());
368
369
###########################################################################
370
} elsif ($action eq 'del') {
371 3843
    my $otherUser = check_user($otherUserID, $otherUserLogin);
372
    $otherUserID = $otherUser->id;
373 3168
374 4317
    Bugzilla->params->{'allowuserdeletion'} 
375
        || ThrowUserError('users_deletion_disabled');
376 3168
    $editusers || ThrowUserError('auth_failure', {group  => "editusers",
377
                                                  action => "delete",
378
                                                  object => "users"});
379
    $vars->{'otheruser'}      = $otherUser;
380
381
    # Find other cross references.
382 6324
    $vars->{'attachments'} = $dbh->selectrow_array(
383
        'SELECT COUNT(*) FROM attachments WHERE submitter_id = ?',
384
        undef, $otherUserID);
385 3571
    $vars->{'assignee_or_qa'} = $dbh->selectrow_array(
386 3168
        qq{SELECT COUNT(*)
387
           FROM bugs
388 3571
           WHERE assigned_to = ? OR qa_contact = ?},
389
        undef, ($otherUserID, $otherUserID));
390
    $vars->{'reporter'} = $dbh->selectrow_array(
391
        'SELECT COUNT(*) FROM bugs WHERE reporter = ?',
392
        undef, $otherUserID);
393 3168
    $vars->{'cc'} = $dbh->selectrow_array(
394
        'SELECT COUNT(*) FROM cc WHERE who = ?',
395
        undef, $otherUserID);
396
    $vars->{'bugs_activity'} = $dbh->selectrow_array(
397
        'SELECT COUNT(*) FROM bugs_activity WHERE who = ?',
398
        undef, $otherUserID);
399 6324
    $vars->{'component_cc'} = $dbh->selectrow_array(
400
        'SELECT COUNT(*) FROM component_cc WHERE user_id = ?',
401
        undef, $otherUserID);
402 3866
    $vars->{'email_setting'} = $dbh->selectrow_array(
403
        'SELECT COUNT(*) FROM email_setting WHERE user_id = ?',
404
        undef, $otherUserID);
405 3168
    $vars->{'flags'}{'requestee'} = $dbh->selectrow_array(
406 4125
        'SELECT COUNT(*) FROM flags WHERE requestee_id = ?',
407 3168
        undef, $otherUserID);
408
    $vars->{'flags'}{'setter'} = $dbh->selectrow_array(
409
        'SELECT COUNT(*) FROM flags WHERE setter_id = ?',
410
        undef, $otherUserID);
411
    $vars->{'longdescs'} = $dbh->selectrow_array(
412
        'SELECT COUNT(*) FROM longdescs WHERE who = ?',
413
        undef, $otherUserID);
414 4438
    my $namedquery_ids = $dbh->selectcol_arrayref(
415 4348
        'SELECT id FROM namedqueries WHERE userid = ?',
416 4438
        undef, $otherUserID);
417
    $vars->{'namedqueries'} = scalar(@$namedquery_ids);
418
    if (scalar(@$namedquery_ids)) {
419 4348
        $vars->{'namedquery_group_map'} = $dbh->selectrow_array(
420
            'SELECT COUNT(*) FROM namedquery_group_map WHERE namedquery_id IN' .
421 4438
            ' (' . join(', ', @$namedquery_ids) . ')');
422 4348
    }
423
    else {
424
        $vars->{'namedquery_group_map'} = 0;
425
    }
426 3866
    $vars->{'profile_setting'} = $dbh->selectrow_array(
427
        'SELECT COUNT(*) FROM profile_setting WHERE user_id = ?',
428
        undef, $otherUserID);
429 3168
    $vars->{'profiles_activity'} = $dbh->selectrow_array(
430
        'SELECT COUNT(*) FROM profiles_activity WHERE who = ? AND userid != ?',
431
        undef, ($otherUserID, $otherUserID));
432 6324
    $vars->{'quips'} = $dbh->selectrow_array(
433
        'SELECT COUNT(*) FROM quips WHERE userid = ?',
434
        undef, $otherUserID);
435 3168
    $vars->{'series'} = $dbh->selectrow_array(
436
        'SELECT COUNT(*) FROM series WHERE creator = ?',
437
        undef, $otherUserID);
438
    $vars->{'watch'}{'watched'} = $dbh->selectrow_array(
439
        'SELECT COUNT(*) FROM watch WHERE watched = ?',
440
        undef, $otherUserID);
441
    $vars->{'watch'}{'watcher'} = $dbh->selectrow_array(
442
        'SELECT COUNT(*) FROM watch WHERE watcher = ?',
443
        undef, $otherUserID);
444
    $vars->{'whine_events'} = $dbh->selectrow_array(
445
        'SELECT COUNT(*) FROM whine_events WHERE owner_userid = ?',
446
        undef, $otherUserID);
447
    $vars->{'whine_schedules'} = $dbh->selectrow_array(
448
        qq{SELECT COUNT(distinct eventid)
449
           FROM whine_schedules
450
           WHERE mailto = ?
451
           AND mailto_type = ?
452
          },
453
        undef, ($otherUserID, MAILTO_USER));
454 4612
    $vars->{'token'} = issue_session_token('delete_user');
455 3168
456
    $template->process('admin/users/confirm-delete.html.tmpl', $vars)
457
       || ThrowTemplateError($template->error());
458
459
###########################################################################
460
} elsif ($action eq 'delete') {
461 4612
    check_token_data($token, 'delete_user');
462 3843
    my $otherUser = check_user($otherUserID, $otherUserLogin);
463
    $otherUserID = $otherUser->id;
464 3168
465 3632
    # Cache for user accounts.
466
    my %usercache = (0 => new Bugzilla::User());
467
    my %updatedbugs;
468
469 3168
    # Lock tables during the check+removal session.
470 3192
    # XXX: if there was some change on these tables after the deletion
471
    #      confirmation checks, we may do something here we haven't warned
472
    #      about.
473 5289
    $dbh->bz_start_transaction();
474 3168
475 4317
    Bugzilla->params->{'allowuserdeletion'}
476 3180
        || ThrowUserError('users_deletion_disabled');
477 3168
    $editusers || ThrowUserError('auth_failure',
478
                                 {group  => "editusers",
479
                                  action => "delete",
480 3180
                                  object => "users"});
481 3448
    @{$otherUser->product_responsibilities()}
482 3180
        && ThrowUserError('user_has_responsibility');
483 3168
484 3843
    Bugzilla->logout_user($otherUser);
485 3168
486 4348
    # Get the named query list so we can delete namedquery_group_map entries.
487 4427
    my $namedqueries_as_string = join(', ', @{$dbh->selectcol_arrayref(
488
        'SELECT id FROM namedqueries WHERE userid = ?', undef, $otherUserID)});
489 4348
490 3632
    # Get the timestamp for LogActivityEntry.
491
    my $timestamp = $dbh->selectrow_array('SELECT NOW()');
492
493
    # When we update a bug_activity entry, we update the bug timestamp, too.
494
    my $sth_set_bug_timestamp =
495
        $dbh->prepare('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?');
496
497 6670
    my $sth_updateFlag = $dbh->prepare('INSERT INTO bugs_activity
498
                  (bug_id, attach_id, who, bug_when, fieldid, removed, added)
499
                  VALUES (?, ?, ?, ?, ?, ?, ?)');
500
501
    # Flags
502
    my $flag_ids =
503
      $dbh->selectcol_arrayref('SELECT id FROM flags WHERE requestee_id = ?',
504
                                undef, $otherUserID);
505
506
    my $flags = Bugzilla::Flag->new_from_list($flag_ids);
507
508
    $dbh->do('UPDATE flags SET requestee_id = NULL, modification_date = ?
509
              WHERE requestee_id = ?', undef, ($timestamp, $otherUserID));
510
511
    # We want to remove the requestee but leave the requester alone,
512
    # so we have to log these changes manually.
513
    my %bugs;
514
    push(@{$bugs{$_->bug_id}->{$_->attach_id || 0}}, $_) foreach @$flags;
515
    my $fieldid = get_field_id('flagtypes.name');
516
    foreach my $bug_id (keys %bugs) {
517
        foreach my $attach_id (keys %{$bugs{$bug_id}}) {
518
            my @old_summaries = Bugzilla::Flag->snapshot($bugs{$bug_id}->{$attach_id});
519
            $_->_set_requestee() foreach @{$bugs{$bug_id}->{$attach_id}};
520
            my @new_summaries = Bugzilla::Flag->snapshot($bugs{$bug_id}->{$attach_id});
521
            my ($removed, $added) =
522
              Bugzilla::Flag->update_activity(\@old_summaries, \@new_summaries);
523
            $sth_updateFlag->execute($bug_id, $attach_id || undef, $userid,
524
                                     $timestamp, $fieldid, $removed, $added);
525
        }
526
        $sth_set_bug_timestamp->execute($timestamp, $bug_id);
527 3632
        $updatedbugs{$bug_id} = 1;
528
    }
529 3168
530
    # Simple deletions in referred tables.
531 3866
    $dbh->do('DELETE FROM email_setting WHERE user_id = ?', undef,
532
             $otherUserID);
533 3168
    $dbh->do('DELETE FROM logincookies WHERE userid = ?', undef, $otherUserID);
534
    $dbh->do('DELETE FROM namedqueries WHERE userid = ?', undef, $otherUserID);
535 4348
    $dbh->do('DELETE FROM namedqueries_link_in_footer WHERE user_id = ?', undef,
536
             $otherUserID);
537
    if ($namedqueries_as_string) {
538
        $dbh->do('DELETE FROM namedquery_group_map WHERE namedquery_id IN ' .
539
                 "($namedqueries_as_string)");
540
    }
541 3866
    $dbh->do('DELETE FROM profile_setting WHERE user_id = ?', undef,
542
             $otherUserID);
543 3168
    $dbh->do('DELETE FROM profiles_activity WHERE userid = ? OR who = ?', undef,
544
             ($otherUserID, $otherUserID));
545
    $dbh->do('DELETE FROM tokens WHERE userid = ?', undef, $otherUserID);
546
    $dbh->do('DELETE FROM user_group_map WHERE user_id = ?', undef,
547
             $otherUserID);
548
    $dbh->do('DELETE FROM watch WHERE watcher = ? OR watched = ?', undef,
549
             ($otherUserID, $otherUserID));
550
551 3632
    # Deletions in referred tables which need LogActivityEntry.
552 6670
    my $buglist = $dbh->selectcol_arrayref('SELECT bug_id FROM cc WHERE who = ?',
553
                                            undef, $otherUserID);
554 3632
    $dbh->do('DELETE FROM cc WHERE who = ?', undef, $otherUserID);
555
    foreach my $bug_id (@$buglist) {
556
        LogActivityEntry($bug_id, 'cc', $otherUser->login, '', $userid,
557
                         $timestamp);
558
        $sth_set_bug_timestamp->execute($timestamp, $bug_id);
559
        $updatedbugs{$bug_id} = 1;
560
    }
561
562
    # Even more complex deletions in referred tables.
563 3168
    my $id;
564
565
    # 1) Series
566
    my $sth_seriesid = $dbh->prepare(
567
           'SELECT series_id FROM series WHERE creator = ?');
568
    my $sth_deleteSeries = $dbh->prepare(
569
           'DELETE FROM series WHERE series_id = ?');
570
    my $sth_deleteSeriesData = $dbh->prepare(
571
           'DELETE FROM series_data WHERE series_id = ?');
572
573
    $sth_seriesid->execute($otherUserID);
574
    while ($id = $sth_seriesid->fetchrow_array()) {
575
        $sth_deleteSeriesData->execute($id);
576
        $sth_deleteSeries->execute($id);
577
    }
578
579
    # 2) Whines
580
    my $sth_whineidFromEvents = $dbh->prepare(
581
           'SELECT id FROM whine_events WHERE owner_userid = ?');
582
    my $sth_deleteWhineEvent = $dbh->prepare(
583
           'DELETE FROM whine_events WHERE id = ?');
584
    my $sth_deleteWhineQuery = $dbh->prepare(
585
           'DELETE FROM whine_queries WHERE eventid = ?');
586
    my $sth_deleteWhineSchedule = $dbh->prepare(
587
           'DELETE FROM whine_schedules WHERE eventid = ?');
588
589 5331
    $dbh->do('DELETE FROM whine_schedules WHERE mailto = ? AND mailto_type = ?',
590
             undef, ($otherUserID, MAILTO_USER));
591 3168
592
    $sth_whineidFromEvents->execute($otherUserID);
593
    while ($id = $sth_whineidFromEvents->fetchrow_array()) {
594
        $sth_deleteWhineQuery->execute($id);
595
        $sth_deleteWhineSchedule->execute($id);
596
        $sth_deleteWhineEvent->execute($id);
597
    }
598
599 3571
    # 3) Bugs
600
    # 3.1) fall back to the default assignee
601 3632
    $buglist = $dbh->selectall_arrayref(
602 3571
        'SELECT bug_id, initialowner
603
         FROM bugs
604
         INNER JOIN components ON components.id = bugs.component_id
605
         WHERE assigned_to = ?', undef, $otherUserID);
606
607
    my $sth_updateAssignee = $dbh->prepare(
608 3632
        'UPDATE bugs SET assigned_to = ?, delta_ts = ? WHERE bug_id = ?');
609 3571
610
    foreach my $bug (@$buglist) {
611 3632
        my ($bug_id, $default_assignee_id) = @$bug;
612
        $sth_updateAssignee->execute($default_assignee_id,
613
                                     $timestamp, $bug_id);
614
        $updatedbugs{$bug_id} = 1;
615
        $default_assignee_id ||= 0;
616
        $usercache{$default_assignee_id} ||=
617
            new Bugzilla::User($default_assignee_id);
618
        LogActivityEntry($bug_id, 'assigned_to', $otherUser->login,
619
                         $usercache{$default_assignee_id}->login,
620
                         $userid, $timestamp);
621 3571
    }
622
623
    # 3.2) fall back to the default QA contact
624
    $buglist = $dbh->selectall_arrayref(
625
        'SELECT bug_id, initialqacontact
626
         FROM bugs
627
         INNER JOIN components ON components.id = bugs.component_id
628
         WHERE qa_contact = ?', undef, $otherUserID);
629
630
    my $sth_updateQAcontact = $dbh->prepare(
631 3632
        'UPDATE bugs SET qa_contact = ?, delta_ts = ? WHERE bug_id = ?');
632 3571
633
    foreach my $bug (@$buglist) {
634 3632
        my ($bug_id, $default_qa_contact_id) = @$bug;
635
        $sth_updateQAcontact->execute($default_qa_contact_id,
636
                                      $timestamp, $bug_id);
637
        $updatedbugs{$bug_id} = 1;
638
        $default_qa_contact_id ||= 0;
639
        $usercache{$default_qa_contact_id} ||=
640
            new Bugzilla::User($default_qa_contact_id);
641
        LogActivityEntry($bug_id, 'qa_contact', $otherUser->login,
642
                         $usercache{$default_qa_contact_id}->login,
643
                         $userid, $timestamp);
644 3571
    }
645
646 3168
    # Finally, remove the user account itself.
647
    $dbh->do('DELETE FROM profiles WHERE userid = ?', undef, $otherUserID);
648
649 5289
    $dbh->bz_commit_transaction();
650 4612
    delete_token($token);
651 3168
652
    $vars->{'message'} = 'account_deleted';
653 3843
    $vars->{'otheruser'}{'login'} = $otherUser->login;
654 3596
    $vars->{'restrictablegroups'} = $user->bless_groups();
655 3168
    $template->process('admin/users/search.html.tmpl', $vars)
656
       || ThrowTemplateError($template->error());
657
658 3632
    # Send mail about what we've done to bugs.
659
    # The deleted user is not notified of the changes.
660
    foreach (keys(%updatedbugs)) {
661 7150
        Bugzilla::BugMail::Send($_, {'changer' => $user} );
662 3632
    }
663
664 3168
###########################################################################
665 4009
} elsif ($action eq 'activity') {
666
    my $otherUser = check_user($otherUserID, $otherUserLogin);
667
668
    $vars->{'profile_changes'} = $dbh->selectall_arrayref(
669
        "SELECT profiles.login_name AS who, " .
670
                $dbh->sql_date_format('profiles_activity.profiles_when') . " AS activity_when,
671 6073
                fielddefs.name AS what,
672 4009
                profiles_activity.oldvalue AS removed,
673
                profiles_activity.newvalue AS added
674
         FROM profiles_activity
675
         INNER JOIN profiles ON profiles_activity.who = profiles.userid
676 4385
         INNER JOIN fielddefs ON fielddefs.id = profiles_activity.fieldid
677 4009
         WHERE profiles_activity.userid = ?
678
         ORDER BY profiles_activity.profiles_when",
679
        {'Slice' => {}},
680
        $otherUser->id);
681
682
    $vars->{'otheruser'} = $otherUser;
683
684
    $template->process("account/profile-activity.html.tmpl", $vars)
685
        || ThrowTemplateError($template->error());
686
687
###########################################################################
688 3168
} else {
689 7188
    ThrowUserError('unknown_action', {action => $action});
690 3168
}
691
692
exit;
693
694
###########################################################################
695
# Helpers
696
###########################################################################
697
698 3843
# Try to build a user object using its ID, else its login name, and throw
699
# an error if the user does not exist.
700
sub check_user {
701
    my ($otherUserID, $otherUserLogin) = @_;
702
703
    my $otherUser;
704
    my $vars = {};
705
706
    if ($otherUserID) {
707
        $otherUser = Bugzilla::User->new($otherUserID);
708
        $vars->{'user_id'} = $otherUserID;
709
    }
710
    elsif ($otherUserLogin) {
711 4444
        $otherUser = new Bugzilla::User({ name => $otherUserLogin });
712 3843
        $vars->{'user_login'} = $otherUserLogin;
713
    }
714 8463
    ($otherUser && $otherUser->id) || ThrowUserError('invalid_user', $vars);
715 3843
716
    return $otherUser;
717
}
718
719 3168
# Copy incoming list selection values from CGI params to template variables.
720
sub mirrorListSelectionValues {
721 4329
    my $cgi = Bugzilla->cgi;
722 3168
    if (defined($cgi->param('matchtype'))) {
723 3987
        foreach ('matchvalue', 'matchstr', 'matchtype', 'grouprestrict', 'groupid') {
724 3168
            $vars->{'listselectionvalues'}{$_} = $cgi->param($_);
725
        }
726
    }
727
}
728
729
# Retrieve user data for the user editing form. User creation and user
730
# editing code rely on this to call derive_groups().
731
sub userDataToVars {
732 3539
    my $otheruserid = shift;
733
    my $otheruser = new Bugzilla::User($otheruserid);
734 3168
    my $query;
735 4329
    my $user = Bugzilla->user;
736 3255
    my $dbh = Bugzilla->dbh;
737 3168
738 3604
    my $grouplist = $otheruser->groups_as_string;
739 3168
740 3539
    $vars->{'otheruser'} = $otheruser;
741 3596
    $vars->{'groups'} = $user->bless_groups();
742 3168
743
    $vars->{'permissions'} = $dbh->selectall_hashref(
744
        qq{SELECT id,
745
                  COUNT(directmember.group_id) AS directmember,
746
                  COUNT(regexpmember.group_id) AS regexpmember,
747 3619
                  (CASE WHEN (groups.id IN ($grouplist)
748
                              AND COUNT(directmember.group_id) = 0
749
                              AND COUNT(regexpmember.group_id) = 0
750
                             ) THEN 1 ELSE 0 END) 
751
                      AS derivedmember,
752 3168
                  COUNT(directbless.group_id) AS directbless
753
           FROM groups
754
           LEFT JOIN user_group_map AS directmember
755
                  ON directmember.group_id = id
756
                 AND directmember.user_id = ?
757
                 AND directmember.isbless = 0
758
                 AND directmember.grant_type = ?
759
           LEFT JOIN user_group_map AS regexpmember
760
                  ON regexpmember.group_id = id
761
                 AND regexpmember.user_id = ?
762
                 AND regexpmember.isbless = 0
763
                 AND regexpmember.grant_type = ?
764
           LEFT JOIN user_group_map AS directbless
765
                  ON directbless.group_id = id
766
                 AND directbless.user_id = ?
767
                 AND directbless.isbless = 1
768
                 AND directbless.grant_type = ?
769 3255
          } . $dbh->sql_group_by('id'),
770 3168
        'id', undef,
771 3539
        ($otheruserid, GRANT_DIRECT,
772
         $otheruserid, GRANT_REGEXP,
773
         $otheruserid, GRANT_DIRECT));
774 3168
775
    # Find indirect bless permission.
776
    $query = qq{SELECT groups.id
777 3604
                FROM groups, group_group_map AS ggm
778
                WHERE groups.id = ggm.grantor_id
779
                  AND ggm.member_id IN ($grouplist)
780 3168
                  AND ggm.grant_type = ?
781 3255
               } . $dbh->sql_group_by('id');
782 3539
    foreach (@{$dbh->selectall_arrayref($query, undef,
783 3604
                                        (GROUP_BLESS))}) {
784 3168
        # Merge indirect bless permissions into permission variable.
785
        $vars->{'permissions'}{${$_}[0]}{'indirectbless'} = 1;
786
    }
787
}
788 3665
789 3843
sub edit_processing {
790
    my $otherUser = shift;
791 4329
    my $user = Bugzilla->user;
792
    my $template = Bugzilla->template;
793 3665
794 4329
    $user->in_group('editusers') || $user->can_see_user($otherUser)
795 3665
        || ThrowUserError('auth_failure', {reason => "not_visible",
796
                                           action => "modify",
797
                                           object => "user"});
798
799 3843
    userDataToVars($otherUser->id);
800 4612
    $vars->{'token'} = issue_session_token('edit_user');
801 3665
802
    $template->process('admin/users/edit.html.tmpl', $vars)
803
       || ThrowTemplateError($template->error());
804
}

Loggerhead 1.18.1 is a web-based interface for Bazaar branches