Merge remote-tracking branch 'github-p/feature/prune-users' into develop

* github-p/feature/prune-users: (21 commits)
  [feature/prune-users] $author_id_sql is no longer needed here.
  [feature/prune-users] Move quote to the correct line.
  [feature/prune-users] Split one line into two lines for readability.
  [feature/prune-users] Use empty for checking array size.
  [feature/prune-users] Fix issues with queries pointed out by bantu.
  [feature/prune-users] Add spaces inside brackets.
  [feature/prune-users] Use a map instead of performing array scans.
  [feature/prune-users] Fix incorrect condition when deleting log entries.
  [feature/prune-users] Non-cosmetic changes per bantu's review.
  [feature/prune-users] Cosmetic changes per bantu's review.
  [feature/prune-users] Minor UI adjustment and bug fixes
  [feature/prune_users] Moved set_config_count out of user loop.
  [feature/prune-users] Fixed user id check in undelivered users loop.
  [feature/prune-users] Call sql_is_set once for author ids in user_delete.
  [feature/prune-users] Replaced missed occurrences of $user_id with $user_ids.
  [feature/prune-users] Call sql_is_set for user ids once in user_delete.
  [feature/prune-users] Fixed whitespace in language file.
  [feature/prune-users] Apply 904bcb86a0.
  [feature/prune-users] Adjust some language strings for new features
  [feature/prune-users] Apply e6ed55a9c1.
  ...
This commit is contained in:
Nils Adermann 2012-11-09 11:47:21 +01:00
commit 128eae7217
7 changed files with 334 additions and 173 deletions

View file

@ -9,7 +9,7 @@
<form id="acp_prune" method="post" action="{U_ACTION}"> <form id="acp_prune" method="post" action="{U_ACTION}">
<fieldset> <fieldset>
<legend>{L_ACP_PRUNE_USERS}</legend> <legend>{L_CRITERIA}</legend>
<dl> <dl>
<dt><label for="username">{L_USERNAME}:</label></dt> <dt><label for="username">{L_USERNAME}:</label></dt>
<dd><input type="text" id="username" name="username" /></dd> <dd><input type="text" id="username" name="username" /></dd>
@ -18,9 +18,16 @@
<dt><label for="email">{L_EMAIL}:</label></dt> <dt><label for="email">{L_EMAIL}:</label></dt>
<dd><input type="text" id="email" name="email" /></dd> <dd><input type="text" id="email" name="email" /></dd>
</dl> </dl>
<dl>
<dt><label for="email">{L_WEBSITE}:</label></dt>
<dd><input type="text" id="website" name="website" /></dd>
</dl>
<dl> <dl>
<dt><label for="joined">{L_JOINED}:</label><br /><span>{L_JOINED_EXPLAIN}</span></dt> <dt><label for="joined">{L_JOINED}:</label><br /><span>{L_JOINED_EXPLAIN}</span></dt>
<dd><select name="joined_select">{S_JOINED_OPTIONS}</select> <input type="text" id="joined" name="joined" /></dd> <dd>
<strong>{L_AFTER}</strong> <input type="text" id="joined_after" name="joined_after" />
<br /> <br /> <strong>{L_BEFORE}</strong> <input type="text" id="joined_before" name="joined_before" />
</dd>
</dl> </dl>
<dl> <dl>
<dt><label for="active">{L_LAST_ACTIVE}:</label><br /><span>{L_LAST_ACTIVE_EXPLAIN}</span></dt> <dt><label for="active">{L_LAST_ACTIVE}:</label><br /><span>{L_LAST_ACTIVE_EXPLAIN}</span></dt>
@ -30,11 +37,29 @@
<dt><label for="count">{L_POSTS}:</label></dt> <dt><label for="count">{L_POSTS}:</label></dt>
<dd><select name="count_select">{S_COUNT_OPTIONS}</select> <input type="text" id="count" name="count" /></dd> <dd><select name="count_select">{S_COUNT_OPTIONS}</select> <input type="text" id="count" name="count" /></dd>
</dl> </dl>
<dl>
<dt><label for="posts_on_queue">{L_POSTS_ON_QUEUE}:</label></dt>
<dd><select name="queue_select">{S_COUNT_OPTIONS}</select> <input type="text" id="posts_on_queue" name="posts_on_queue" /></select>
</dl>
<!-- IF S_GROUP_LIST -->
<dl>
<dt><label for="group_id">{L_GROUP}:</label><br /><span>{L_PRUNE_USERS_GROUP_EXPLAIN}</dt>
<dd><select name="group_id">{S_GROUP_LIST}</select></dd>
</dl>
<!-- ENDIF -->
</fieldset>
<fieldset>
<legend>{L_USERNAMES}</legend>
<dl> <dl>
<dt><label for="users">{L_ACP_PRUNE_USERS}:</label><br /><span>{L_SELECT_USERS_EXPLAIN}</span></dt> <dt><label for="users">{L_ACP_PRUNE_USERS}:</label><br /><span>{L_SELECT_USERS_EXPLAIN}</span></dt>
<dd><textarea id="users" name="users" cols="40" rows="5"></textarea></dd> <dd><textarea id="users" name="users" cols="40" rows="5"></textarea></dd>
<dd>[ <a href="{U_FIND_USERNAME}" onclick="find_username(this.href); return false;">{L_FIND_USERNAME}</a> ]</dd> <dd>[ <a href="{U_FIND_USERNAME}" onclick="find_username(this.href); return false;">{L_FIND_USERNAME}</a> ]</dd>
</dl> </dl>
</fieldset>
<fieldset>
<legend>{L_OPTIONS}</legend>
<dl> <dl>
<dt><label for="deleteposts">{L_DELETE_USER_POSTS}:</label><br /><span>{L_DELETE_USER_POSTS_EXPLAIN}</span></dt> <dt><label for="deleteposts">{L_DELETE_USER_POSTS}:</label><br /><span>{L_DELETE_USER_POSTS_EXPLAIN}</span></dt>
<dd><label><input type="radio" class="radio" name="deleteposts" value="1" /> {L_YES}</label> <dd><label><input type="radio" class="radio" name="deleteposts" value="1" /> {L_YES}</label>

View file

@ -2,6 +2,23 @@
<form id="confirm" method="post" action="{S_CONFIRM_ACTION}"> <form id="confirm" method="post" action="{S_CONFIRM_ACTION}">
<fieldset id="userlist">
<h2>{L_PRUNE_USERS_LIST}</h2>
<!-- IF S_DEACTIVATE --><p>{L_PRUNE_USERS_LIST_DEACTIVATE}</p><!-- ELSE --><p>{L_PRUNE_USERS_LIST_DELETE}</p><!-- ENDIF -->
<br />
<!-- BEGIN users -->
&raquo; <input type="checkbox" name="user_ids[]" value="{users.USER_ID}" checked="checked" />
<a href="{users.U_PROFILE}">{users.USERNAME}</a>
<!-- IF users.U_USER_ADMIN --> [ <a href="{users.U_USER_ADMIN}">{L_USER_ADMIN}</a> ]<!-- ENDIF --><br />
<!-- END users -->
<br />
<span class="small">
<a href="#" onclick="marklist('userlist', 'user_ids', true)">{L_MARK_ALL}</a> &bull;
<a href="#" onclick="marklist('userlist', 'user_ids', false)">{L_UNMARK_ALL}</a>
</span>
</fieldset>
<fieldset> <fieldset>
<h1>{MESSAGE_TITLE}</h1> <h1>{MESSAGE_TITLE}</h1>
<p>{MESSAGE_TEXT}</p> <p>{MESSAGE_TEXT}</p>
@ -12,17 +29,6 @@
<input type="submit" name="confirm" value="{L_YES}" class="button2" />&nbsp; <input type="submit" name="confirm" value="{L_YES}" class="button2" />&nbsp;
<input type="submit" name="cancel" value="{L_NO}" class="button2" /> <input type="submit" name="cancel" value="{L_NO}" class="button2" />
</div> </div>
<h2>{L_PRUNE_USERS_LIST}</h2>
<!-- IF S_DEACTIVATE --><p>{L_PRUNE_USERS_LIST_DEACTIVATE}</p><!-- ELSE --><p>{L_PRUNE_USERS_LIST_DELETE}</p><!-- ENDIF -->
<br />
<!-- BEGIN users -->
&raquo; <a href="{users.U_PROFILE}">{users.USERNAME}</a><!-- IF users.U_USER_ADMIN --> [<a href="{users.U_USER_ADMIN}">{L_USER_ADMIN}</a>]<!-- ENDIF --><br />
<!-- END users -->
<br /><br />
</fieldset> </fieldset>
</form> </form>

View file

@ -155,10 +155,7 @@ class acp_inactive
trigger_error($user->lang['NO_AUTH_OPERATION'] . adm_back_link($this->u_action), E_USER_WARNING); trigger_error($user->lang['NO_AUTH_OPERATION'] . adm_back_link($this->u_action), E_USER_WARNING);
} }
foreach ($mark as $user_id) user_delete('retain', $mark, true);
{
user_delete('retain', $user_id, $user_affected[$user_id]);
}
add_log('admin', 'LOG_INACTIVE_' . strtoupper($action), implode(', ', $user_affected)); add_log('admin', 'LOG_INACTIVE_' . strtoupper($action), implode(', ', $user_affected));

View file

@ -242,8 +242,8 @@ class acp_prune
if (confirm_box(true)) if (confirm_box(true))
{ {
$user_ids = $usernames = array(); $user_ids = $usernames = array();
$this->get_prune_users($user_ids, $usernames);
$this->get_prune_users($user_ids, $usernames);
if (sizeof($user_ids)) if (sizeof($user_ids))
{ {
if ($action == 'deactivate') if ($action == 'deactivate')
@ -255,19 +255,13 @@ class acp_prune
{ {
if ($deleteposts) if ($deleteposts)
{ {
foreach ($user_ids as $user_id) user_delete('remove', $user_ids);
{
user_delete('remove', $user_id);
}
$l_log = 'LOG_PRUNE_USER_DEL_DEL'; $l_log = 'LOG_PRUNE_USER_DEL_DEL';
} }
else else
{ {
foreach ($user_ids as $user_id) user_delete('retain', $user_ids, true);
{
user_delete('retain', $user_id, $usernames[$user_id]);
}
$l_log = 'LOG_PRUNE_USER_DEL_ANON'; $l_log = 'LOG_PRUNE_USER_DEL_ANON';
} }
@ -299,6 +293,7 @@ class acp_prune
{ {
$template->assign_block_vars('users', array( $template->assign_block_vars('users', array(
'USERNAME' => $usernames[$user_id], 'USERNAME' => $usernames[$user_id],
'USER_ID' => $user_id,
'U_PROFILE' => append_sid($phpbb_root_path . 'memberlist.' . $phpEx, 'mode=viewprofile&amp;u=' . $user_id), 'U_PROFILE' => append_sid($phpbb_root_path . 'memberlist.' . $phpEx, 'mode=viewprofile&amp;u=' . $user_id),
'U_USER_ADMIN' => ($auth->acl_get('a_user')) ? append_sid("{$phpbb_admin_path}index.$phpEx", 'i=users&amp;mode=overview&amp;u=' . $user_id, true, $user->session_id) : '', 'U_USER_ADMIN' => ($auth->acl_get('a_user')) ? append_sid("{$phpbb_admin_path}index.$phpEx", 'i=users&amp;mode=overview&amp;u=' . $user_id, true, $user->session_id) : '',
)); ));
@ -314,17 +309,7 @@ class acp_prune
'mode' => $mode, 'mode' => $mode,
'prune' => 1, 'prune' => 1,
'users' => utf8_normalize_nfc(request_var('users', '', true)),
'username' => utf8_normalize_nfc(request_var('username', '', true)),
'email' => request_var('email', ''),
'joined_select' => request_var('joined_select', ''),
'joined' => request_var('joined', ''),
'active_select' => request_var('active_select', ''),
'active' => request_var('active', ''),
'count_select' => request_var('count_select', ''),
'count' => request_var('count', ''),
'deleteposts' => request_var('deleteposts', 0), 'deleteposts' => request_var('deleteposts', 0),
'action' => request_var('action', ''), 'action' => request_var('action', ''),
)), 'confirm_body_prune.html'); )), 'confirm_body_prune.html');
} }
@ -340,22 +325,29 @@ class acp_prune
} }
$find_time = array('lt' => $user->lang['BEFORE'], 'gt' => $user->lang['AFTER']); $find_time = array('lt' => $user->lang['BEFORE'], 'gt' => $user->lang['AFTER']);
$s_find_join_time = '';
foreach ($find_time as $key => $value)
{
$s_find_join_time .= '<option value="' . $key . '">' . $value . '</option>';
}
$s_find_active_time = ''; $s_find_active_time = '';
foreach ($find_time as $key => $value) foreach ($find_time as $key => $value)
{ {
$s_find_active_time .= '<option value="' . $key . '">' . $value . '</option>'; $s_find_active_time .= '<option value="' . $key . '">' . $value . '</option>';
} }
$s_group_list = '';
$sql = 'SELECT group_id, group_name
FROM ' . GROUPS_TABLE . '
WHERE group_type <> ' . GROUP_SPECIAL . '
ORDER BY group_name ASC';
$result = $db->sql_query($sql);
while ($row = $db->sql_fetchrow($result))
{
$s_group_list .= '<option value="' . $row['group_id'] . '">' . $row['group_name'] . '</select>';
}
$db->sql_freeresult($result);
$template->assign_vars(array( $template->assign_vars(array(
'U_ACTION' => $this->u_action, 'U_ACTION' => $this->u_action,
'S_JOINED_OPTIONS' => $s_find_join_time,
'S_ACTIVE_OPTIONS' => $s_find_active_time, 'S_ACTIVE_OPTIONS' => $s_find_active_time,
'S_GROUP_LIST' => $s_group_list,
'S_COUNT_OPTIONS' => $s_find_count, 'S_COUNT_OPTIONS' => $s_find_count,
'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&amp;form=acp_prune&amp;field=users'), 'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&amp;form=acp_prune&amp;field=users'),
)); ));
@ -368,42 +360,80 @@ class acp_prune
{ {
global $user, $db; global $user, $db;
$users = utf8_normalize_nfc(request_var('users', '', true)); $users_by_name = request_var('users', '', true);
$users_by_id = request_var('user_ids', array(0));
$group_id = request_var('group_id', 0);
$posts_on_queue = request_var('posts_on_queue', 0);
if ($users) if ($users_by_name)
{ {
$users = explode("\n", $users); $users = explode("\n", $users_by_name);
$where_sql = ' AND ' . $db->sql_in_set('username_clean', array_map('utf8_clean_string', $users)); $where_sql = ' AND ' . $db->sql_in_set('username_clean', array_map('utf8_clean_string', $users));
} }
else if (!empty($users_by_id))
{
$user_ids = $users_by_id;
user_get_id_name($user_ids, $usernames);
$where_sql = ' AND ' . $db->sql_in_set('user_id', $user_ids);
}
else else
{ {
$username = utf8_normalize_nfc(request_var('username', '', true)); $username = request_var('username', '', true);
$email = request_var('email', ''); $email = request_var('email', '');
$website = request_var('website', '');
$joined_select = request_var('joined_select', 'lt');
$active_select = request_var('active_select', 'lt'); $active_select = request_var('active_select', 'lt');
$count_select = request_var('count_select', 'eq'); $count_select = request_var('count_select', 'eq');
$joined = request_var('joined', ''); $queue_select = request_var('queue_select', 'gt');
$joined_before = request_var('joined_before', '');
$joined_after = request_var('joined_after', '');
$active = request_var('active', ''); $active = request_var('active', '');
$active = ($active) ? explode('-', $active) : array(); $count = request_var('count', 0);
$joined = ($joined) ? explode('-', $joined) : array();
if ((sizeof($active) && sizeof($active) != 3) || (sizeof($joined) && sizeof($joined) != 3)) $active = ($active) ? explode('-', $active) : array();
$joined_before = ($joined_before) ? explode('-', $joined_before) : array();
$joined_after = ($joined_after) ? explode('-', $joined_after) : array();
// calculate the conditions required by the join time criteria
$joined_sql = '';
if (!empty($joined_before) && !empty($joined_after))
{
// if the two entered dates are equal, we need to adjust
// so that our time range is a full day instead of 1 second
if ($joined_after == $joined_before)
{
$joined_after[2] += 1;
}
$joined_sql = ' AND user_regdate BETWEEN ' . gmmktime(0, 0, 0, (int) $joined_after[1], (int) $joined_after[2], (int) $joined_after[0]) .
' AND ' . gmmktime(0, 0, 0, (int) $joined_before[1], (int) $joined_before[2], (int) $joined_before[0]);
}
else if (empty($joined_before) && !empty($joined_after))
{
$joined_sql = ' AND user_regdate > ' . gmmktime(0, 0, 0, (int) $joined_after[1], (int) $joined_after[2], (int) $joined_after[0]);
}
else if (empty($joined_after) && !empty($joined_before))
{
$joined_sql = ' AND user_regdate < ' . gmmktime(0, 0, 0, (int) $joined_before[1], (int) $joined_before[2], (int) $joined_before[0]);
}
// implicit else when both arrays are empty do nothing
if ((sizeof($active) && sizeof($active) != 3) || (sizeof($joined_before) && sizeof($joined_before) != 3) || (sizeof($joined_after) && sizeof($joined_after) != 3))
{ {
trigger_error($user->lang['WRONG_ACTIVE_JOINED_DATE'] . adm_back_link($this->u_action), E_USER_WARNING); trigger_error($user->lang['WRONG_ACTIVE_JOINED_DATE'] . adm_back_link($this->u_action), E_USER_WARNING);
} }
$count = request_var('count', '');
$key_match = array('lt' => '<', 'gt' => '>', 'eq' => '='); $key_match = array('lt' => '<', 'gt' => '>', 'eq' => '=');
$sort_by_types = array('username', 'user_email', 'user_posts', 'user_regdate', 'user_lastvisit'); $sort_by_types = array('username', 'user_email', 'user_posts', 'user_regdate', 'user_lastvisit');
$where_sql = ''; $where_sql = '';
$where_sql .= ($username) ? ' AND username_clean ' . $db->sql_like_expression(str_replace('*', $db->any_char, utf8_clean_string($username))) : ''; $where_sql .= ($username) ? ' AND username_clean ' . $db->sql_like_expression(str_replace('*', $db->any_char, utf8_clean_string($username))) : '';
$where_sql .= ($email) ? ' AND user_email ' . $db->sql_like_expression(str_replace('*', $db->any_char, $email)) . ' ' : ''; $where_sql .= ($email) ? ' AND user_email ' . $db->sql_like_expression(str_replace('*', $db->any_char, $email)) . ' ' : '';
$where_sql .= (sizeof($joined)) ? " AND user_regdate " . $key_match[$joined_select] . ' ' . gmmktime(0, 0, 0, (int) $joined[1], (int) $joined[2], (int) $joined[0]) : ''; $where_sql .= ($website) ? ' AND user_website ' . $db->sql_like_expression(str_replace('*', $db->any_char, $website)) . ' ' : '';
$where_sql .= ($count !== '') ? " AND user_posts " . $key_match[$count_select] . ' ' . (int) $count . ' ' : ''; $where_sql .= $joined_sql;
$where_sql .= ($count) ? " AND user_posts " . $key_match[$count_select] . ' ' . (int) $count . ' ' : '';
// First handle pruning of users who never logged in, last active date is 0000-00-00 // First handle pruning of users who never logged in, last active date is 0000-00-00
if (sizeof($active) && (int) $active[0] == 0 && (int) $active[1] == 0 && (int) $active[2] == 0) if (sizeof($active) && (int) $active[0] == 0 && (int) $active[1] == 0 && (int) $active[2] == 0)
@ -446,7 +476,6 @@ class acp_prune
$where_sql"; $where_sql";
$result = $db->sql_query($sql); $result = $db->sql_query($sql);
$where_sql = '';
$user_ids = $usernames = array(); $user_ids = $usernames = array();
while ($row = $db->sql_fetchrow($result)) while ($row = $db->sql_fetchrow($result))
@ -459,5 +488,53 @@ class acp_prune
} }
} }
$db->sql_freeresult($result); $db->sql_freeresult($result);
if ($group_id)
{
$sql = 'SELECT user_id
FROM ' . USER_GROUP_TABLE . '
WHERE group_id = ' . (int) $group_id . '
AND user_pending = 0
AND ' . $db->sql_in_set('user_id', $user_ids, false, true);
$result = $db->sql_query($sql);
// we're performing an intersection operation, so all the relevant users
// come from this most recent query (which was limited to the results of the
// previous query)
$user_ids = $usernames = array();
while ($row = $db->sql_fetchrow($result))
{
$user_ids[] = $row['poster_id'];
}
$db->sql_freeresult($result);
// only get usernames if they are needed (not part of some later query)
if (!$posts_on_queue)
{
// this is an additional query aginst the users table
user_get_id_name($user_ids, $usernames);
}
}
if ($posts_on_queue)
{
$sql = 'SELECT poster_id, COUNT(post_id) AS queue_posts
FROM ' . POSTS_TABLE . '
WHERE ' . $db->sql_in_set('poster_id', $user_ids, false, true) . '
GROUP BY poster_id
HAVING queue_posts ' . $key_match[$queue_select] . ' ' . $posts_on_queue;
$result = $db->sql_query($result);
// same intersection logic as the above group ID portion
$user_ids = $usernames = array();
while ($row = $db->sql_fetchrow($result))
{
$user_ids[] = $row['poster_id'];
}
$db->sql_freeresult($result);
// do an additional query to get the correct set of usernames
user_get_id_name($user_ids, $usernames);
}
} }
} }

View file

@ -1145,6 +1145,23 @@ function phpbb_delete_user_pms($user_id)
return false; return false;
} }
return phpbb_delete_users_pms(array($user_id));
}
/**
* Delete all PM(s) for given users and delete the ones without references
*
* @param array $user_ids IDs of the users whose private messages we want to delete
*
* @return boolean False if there were no pms found, true otherwise.
*/
function phpbb_delete_users_pms($user_ids)
{
global $db, $user, $phpbb_root_path, $phpEx;
$user_id_sql = $db->sql_in_set('user_id', $user_ids);
$author_id_sql = $db->sql_in_set('author_id', $user_ids);
// Get PM Information for later deleting // Get PM Information for later deleting
// The two queries where split, so we can use our indexes // The two queries where split, so we can use our indexes
$undelivered_msg = $delete_ids = array(); $undelivered_msg = $delete_ids = array();
@ -1152,7 +1169,7 @@ function phpbb_delete_user_pms($user_id)
// Part 1: get PMs the user received // Part 1: get PMs the user received
$sql = 'SELECT msg_id $sql = 'SELECT msg_id
FROM ' . PRIVMSGS_TO_TABLE . ' FROM ' . PRIVMSGS_TO_TABLE . '
WHERE user_id = ' . $user_id; WHERE ' . $user_id_sql;
$result = $db->sql_query($sql); $result = $db->sql_query($sql);
while ($row = $db->sql_fetchrow($result)) while ($row = $db->sql_fetchrow($result))
@ -1162,12 +1179,12 @@ function phpbb_delete_user_pms($user_id)
} }
$db->sql_freeresult($result); $db->sql_freeresult($result);
// Part 2: get PMs the user sent, but have yet to be received // Part 2: get PMs the users sent, but are yet to be received.
// We cannot simply delete them. First we have to check, // We cannot simply delete them. First we have to check
// whether another user already received and read the message. // whether another user already received and read the message.
$sql = 'SELECT msg_id $sql = 'SELECT msg_id
FROM ' . PRIVMSGS_TO_TABLE . ' FROM ' . PRIVMSGS_TO_TABLE . '
WHERE author_id = ' . $user_id . ' WHERE ' . $author_id_sql . '
AND folder_id = ' . PRIVMSGS_NO_BOX; AND folder_id = ' . PRIVMSGS_NO_BOX;
$result = $db->sql_query($sql); $result = $db->sql_query($sql);
@ -1193,7 +1210,7 @@ function phpbb_delete_user_pms($user_id)
// received them. // received them.
$sql = 'SELECT msg_id $sql = 'SELECT msg_id
FROM ' . PRIVMSGS_TO_TABLE . ' FROM ' . PRIVMSGS_TO_TABLE . '
WHERE author_id = ' . $user_id . ' WHERE ' . $author_id_sql . '
AND folder_id <> ' . PRIVMSGS_NO_BOX . ' AND folder_id <> ' . PRIVMSGS_NO_BOX . '
AND folder_id <> ' . PRIVMSGS_OUTBOX . ' AND folder_id <> ' . PRIVMSGS_OUTBOX . '
AND folder_id <> ' . PRIVMSGS_SENTBOX; AND folder_id <> ' . PRIVMSGS_SENTBOX;
@ -1213,7 +1230,7 @@ function phpbb_delete_user_pms($user_id)
// Count the messages we delete, so we can correct the user pm data // Count the messages we delete, so we can correct the user pm data
$sql = 'SELECT user_id, COUNT(msg_id) as num_undelivered_privmsgs $sql = 'SELECT user_id, COUNT(msg_id) as num_undelivered_privmsgs
FROM ' . PRIVMSGS_TO_TABLE . ' FROM ' . PRIVMSGS_TO_TABLE . '
WHERE author_id = ' . $user_id . ' WHERE ' . $author_id_sql . '
AND folder_id = ' . PRIVMSGS_NO_BOX . ' AND folder_id = ' . PRIVMSGS_NO_BOX . '
AND ' . $db->sql_in_set('msg_id', array_merge($undelivered_msg, $delivered_msg)) . ' AND ' . $db->sql_in_set('msg_id', array_merge($undelivered_msg, $delivered_msg)) . '
GROUP BY user_id'; GROUP BY user_id';
@ -1271,12 +1288,12 @@ function phpbb_delete_user_pms($user_id)
$sql = 'UPDATE ' . USERS_TABLE . ' $sql = 'UPDATE ' . USERS_TABLE . '
SET user_new_privmsg = 0, SET user_new_privmsg = 0,
user_unread_privmsg = 0 user_unread_privmsg = 0
WHERE user_id = ' . $user_id; WHERE ' . $user_id_sql;
$db->sql_query($sql); $db->sql_query($sql);
// Delete private message data of the user // Delete private message data of the user
$sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . ' $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
WHERE user_id = ' . (int) $user_id; WHERE ' . $user_id_sql;
$db->sql_query($sql); $db->sql_query($sql);
if (!empty($delete_ids)) if (!empty($delete_ids))
@ -1313,12 +1330,12 @@ function phpbb_delete_user_pms($user_id)
// This way users are still able to read messages from users being removed // This way users are still able to read messages from users being removed
$sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . ' $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
SET author_id = ' . ANONYMOUS . ' SET author_id = ' . ANONYMOUS . '
WHERE author_id = ' . $user_id; WHERE ' . $author_id_sql;
$db->sql_query($sql); $db->sql_query($sql);
$sql = 'UPDATE ' . PRIVMSGS_TABLE . ' $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
SET author_id = ' . ANONYMOUS . ' SET author_id = ' . ANONYMOUS . '
WHERE author_id = ' . $user_id; WHERE ' . $author_id_sql;
$db->sql_query($sql); $db->sql_query($sql);
$db->sql_transaction('commit'); $db->sql_transaction('commit');

View file

@ -350,20 +350,34 @@ function user_add($user_row, $cp_data = false)
/** /**
* Remove User * Remove User
* @param $mode Either 'retain' or 'remove'
*/ */
function user_delete($mode, $user_id, $post_username = false) function user_delete($mode, $user_ids, $retain_username = true)
{ {
global $cache, $config, $db, $user, $auth, $phpbb_dispatcher; global $cache, $config, $db, $user, $auth, $phpbb_dispatcher;
global $phpbb_root_path, $phpEx; global $phpbb_root_path, $phpEx;
$db->sql_transaction('begin');
$user_rows = array();
if (!is_array($user_ids))
{
$user_ids = array($user_ids);
}
$user_id_sql = $db->sql_in_set('user_id', $user_ids);
$sql = 'SELECT * $sql = 'SELECT *
FROM ' . USERS_TABLE . ' FROM ' . USERS_TABLE . '
WHERE user_id = ' . $user_id; WHERE ' . $user_id_sql;
$result = $db->sql_query($sql); $result = $db->sql_query($sql);
$user_row = $db->sql_fetchrow($result); while ($row = $db->sql_fetchrow($result))
{
$user_rows[(int) $row['user_id']] = $row;
}
$db->sql_freeresult($result); $db->sql_freeresult($result);
if (!$user_row) if (empty($user_rows))
{ {
return false; return false;
} }
@ -383,7 +397,7 @@ function user_delete($mode, $user_id, $post_username = false)
// Before we begin, we will remove the reports the user issued. // Before we begin, we will remove the reports the user issued.
$sql = 'SELECT r.post_id, p.topic_id $sql = 'SELECT r.post_id, p.topic_id
FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
WHERE r.user_id = ' . $user_id . ' WHERE ' . $db->sql_in_set('r.user_id', $user_ids) . '
AND p.post_id = r.post_id'; AND p.post_id = r.post_id';
$result = $db->sql_query($sql); $result = $db->sql_query($sql);
@ -437,30 +451,44 @@ function user_delete($mode, $user_id, $post_username = false)
} }
// Remove reports // Remove reports
$db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE user_id = ' . $user_id); $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE ' . $user_id_sql);
$num_users_delta = 0;
// Some things need to be done in the loop (if the query changes based
// on which user is currently being deleted)
$added_guest_posts = 0;
foreach ($user_rows as $user_id => $user_row)
{
if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == AVATAR_UPLOAD) if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == AVATAR_UPLOAD)
{ {
avatar_delete('user', $user_row); avatar_delete('user', $user_row);
} }
// Decrement number of users if this user is active
if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
{
--$num_users_delta;
}
switch ($mode) switch ($mode)
{ {
case 'retain': case 'retain':
if ($retain_username === false)
$db->sql_transaction('begin');
if ($post_username === false)
{ {
$post_username = $user->lang['GUEST']; $post_username = $user->lang['GUEST'];
} }
// If the user is inactive and newly registered we assume no posts from this user being there...
if ($user_row['user_type'] == USER_INACTIVE && $user_row['user_inactive_reason'] == INACTIVE_REGISTER && !$user_row['user_posts'])
{
}
else else
{ {
$post_username = $user_row['username'];
}
// If the user is inactive and newly registered
// we assume no posts from the user, and save
// the queries
if ($user_row['user_type'] != USER_INACTIVE || $user_row['user_inactive_reason'] != INACTIVE_REGISTER || $user_row['user_posts'])
{
// When we delete these users and retain the posts, we must assign all the data to the guest user
$sql = 'UPDATE ' . FORUMS_TABLE . ' $sql = 'UPDATE ' . FORUMS_TABLE . '
SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = '' SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = ''
WHERE forum_last_poster_id = $user_id"; WHERE forum_last_poster_id = $user_id";
@ -471,11 +499,6 @@ function user_delete($mode, $user_id, $post_username = false)
WHERE poster_id = $user_id"; WHERE poster_id = $user_id";
$db->sql_query($sql); $db->sql_query($sql);
$sql = 'UPDATE ' . POSTS_TABLE . '
SET post_edit_user = ' . ANONYMOUS . "
WHERE post_edit_user = $user_id";
$db->sql_query($sql);
$sql = 'UPDATE ' . TOPICS_TABLE . ' $sql = 'UPDATE ' . TOPICS_TABLE . '
SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = '' SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = ''
WHERE topic_poster = $user_id"; WHERE topic_poster = $user_id";
@ -486,48 +509,66 @@ function user_delete($mode, $user_id, $post_username = false)
WHERE topic_last_poster_id = $user_id"; WHERE topic_last_poster_id = $user_id";
$db->sql_query($sql); $db->sql_query($sql);
$sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
SET poster_id = ' . ANONYMOUS . "
WHERE poster_id = $user_id";
$db->sql_query($sql);
// Since we change every post by this author, we need to count this amount towards the anonymous user // Since we change every post by this author, we need to count this amount towards the anonymous user
// Update the post count for the anonymous user
if ($user_row['user_posts']) if ($user_row['user_posts'])
{ {
$sql = 'UPDATE ' . USERS_TABLE . ' $added_guest_posts += $user_row['user_posts'];
SET user_posts = user_posts + ' . $user_row['user_posts'] . '
WHERE user_id = ' . ANONYMOUS;
$db->sql_query($sql);
} }
} }
$db->sql_transaction('commit');
break; break;
case 'remove': case 'remove':
// there is nothing variant specific to deleting posts
break;
}
}
if ($num_users_delta != 0)
{
set_config_count('num_users', $num_users_delta, true);
}
// Now do the invariant tasks
// all queries performed in one call of this function are in a single transaction
// so this is kosher
if ($mode == 'retain')
{
// Assign more data to the Anonymous user
$sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
SET poster_id = ' . ANONYMOUS . '
WHERE ' . $db->sql_in_set('poster_id', $user_ids);
$db->sql_query($sql);
$sql = 'UPDATE ' . POSTS_TABLE . '
SET post_edit_user = ' . ANONYMOUS . '
WHERE ' . $db->sql_in_set('post_edit_user', $user_ids);
$db->sql_query($sql);
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_posts = user_posts + ' . $added_guest_posts . '
WHERE user_id = ' . ANONYMOUS;
$db->sql_query($sql);
}
else if ($mode == 'remove')
{
if (!function_exists('delete_posts')) if (!function_exists('delete_posts'))
{ {
include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
} }
// Delete posts, attachments, etc. // Delete posts, attachments, etc.
delete_posts('poster_id', $user_id); // delete_posts can handle any number of IDs in its second argument
delete_posts('poster_id', $user_ids);
break;
} }
$db->sql_transaction('begin');
$table_ary = array(USERS_TABLE, USER_GROUP_TABLE, TOPICS_WATCH_TABLE, FORUMS_WATCH_TABLE, ACL_USERS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, FORUMS_TRACK_TABLE, PROFILE_FIELDS_DATA_TABLE, MODERATOR_CACHE_TABLE, DRAFTS_TABLE, BOOKMARKS_TABLE, SESSIONS_KEYS_TABLE, PRIVMSGS_FOLDER_TABLE, PRIVMSGS_RULES_TABLE); $table_ary = array(USERS_TABLE, USER_GROUP_TABLE, TOPICS_WATCH_TABLE, FORUMS_WATCH_TABLE, ACL_USERS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, FORUMS_TRACK_TABLE, PROFILE_FIELDS_DATA_TABLE, MODERATOR_CACHE_TABLE, DRAFTS_TABLE, BOOKMARKS_TABLE, SESSIONS_KEYS_TABLE, PRIVMSGS_FOLDER_TABLE, PRIVMSGS_RULES_TABLE);
// Delete the miscellaneous (non-post) data for the user
foreach ($table_ary as $table) foreach ($table_ary as $table)
{ {
$sql = "DELETE FROM $table $sql = "DELETE FROM $table
WHERE user_id = $user_id"; WHERE " . $user_id_sql;
$db->sql_query($sql); $db->sql_query($sql);
} }
@ -535,29 +576,29 @@ function user_delete($mode, $user_id, $post_username = false)
// Delete user log entries about this user // Delete user log entries about this user
$sql = 'DELETE FROM ' . LOG_TABLE . ' $sql = 'DELETE FROM ' . LOG_TABLE . '
WHERE reportee_id = ' . $user_id; WHERE ' . $db->sql_in_set('reportee_id', $user_ids);
$db->sql_query($sql); $db->sql_query($sql);
// Change user_id to anonymous for this users triggered events // Change user_id to anonymous for this users triggered events
$sql = 'UPDATE ' . LOG_TABLE . ' $sql = 'UPDATE ' . LOG_TABLE . '
SET user_id = ' . ANONYMOUS . ' SET user_id = ' . ANONYMOUS . '
WHERE user_id = ' . $user_id; WHERE ' . $user_id_sql;
$db->sql_query($sql); $db->sql_query($sql);
// Delete the user_id from the zebra table // Delete the user_id from the zebra table
$sql = 'DELETE FROM ' . ZEBRA_TABLE . ' $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
WHERE user_id = ' . $user_id . ' WHERE ' . $user_id_sql . '
OR zebra_id = ' . $user_id; OR ' . $db->sql_in_set('zebra_id', $user_ids);
$db->sql_query($sql); $db->sql_query($sql);
// Delete the user_id from the banlist // Delete the user_id from the banlist
$sql = 'DELETE FROM ' . BANLIST_TABLE . ' $sql = 'DELETE FROM ' . BANLIST_TABLE . '
WHERE ban_userid = ' . $user_id; WHERE ' . $db->sql_in_set('ban_userid', $user_ids);
$db->sql_query($sql); $db->sql_query($sql);
// Delete the user_id from the session table // Delete the user_id from the session table
$sql = 'DELETE FROM ' . SESSIONS_TABLE . ' $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
WHERE session_user_id = ' . $user_id; WHERE ' . $db->sql_in_set('session_user_id', $user_ids);
$db->sql_query($sql); $db->sql_query($sql);
// Clean the private messages tables from the user // Clean the private messages tables from the user
@ -565,7 +606,7 @@ function user_delete($mode, $user_id, $post_username = false)
{ {
include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx); include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
} }
phpbb_delete_user_pms($user_id); phpbb_delete_users_pms($user_ids);
$db->sql_transaction('commit'); $db->sql_transaction('commit');
@ -582,17 +623,11 @@ function user_delete($mode, $user_id, $post_username = false)
extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars))); extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars)));
// Reset newest user info if appropriate // Reset newest user info if appropriate
if ($config['newest_user_id'] == $user_id) if (in_array($config['newest_user_id'], $user_ids))
{ {
update_last_username(); update_last_username();
} }
// Decrement number of users if this user is active
if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
{
set_config_count('num_users', -1, true);
}
return false; return false;
} }

View file

@ -36,7 +36,9 @@ if (empty($lang) || !is_array($lang))
// User pruning // User pruning
$lang = array_merge($lang, array( $lang = array_merge($lang, array(
'ACP_PRUNE_USERS_EXPLAIN' => 'This section allows you to delete or deactivate users on your board. Accounts can be filtered in a variety of ways; by post count, most recent activity, etc. Criteria may be combined to narrow down which accounts are affected. For example, you can prune users with fewer than 10 posts, who were also inactive after 2002-01-01. Alternatively, you may skip the criteria selection completely by entering a list of users (each on a separate line) into the text field. Take care with this facility! Once a user is deleted, there is no way to reverse the action.', 'ACP_PRUNE_USERS_EXPLAIN' => 'This section allows you to delete or deactivate users on your board. Accounts can be filtered in a variety of ways; by post count, most recent activity, etc. Criteria may be combined to narrow down which accounts are affected. For example, you can prune users with fewer than 10 posts, who were also inactive after 2002-01-01. Use * as a wildcard for text fields. Alternatively, you may skip the criteria selection completely by entering a list of users (each on a separate line) into the text field. Take care with this facility! Once a user is deleted, there is no way to reverse the action.',
'CRITERIA' => 'Criteria',
'DEACTIVATE_DELETE' => 'Deactivate or delete', 'DEACTIVATE_DELETE' => 'Deactivate or delete',
'DEACTIVATE_DELETE_EXPLAIN' => 'Choose whether to deactivate users or delete them entirely. Please note that deleted users cannot be restored!', 'DEACTIVATE_DELETE_EXPLAIN' => 'Choose whether to deactivate users or delete them entirely. Please note that deleted users cannot be restored!',
@ -44,15 +46,17 @@ $lang = array_merge($lang, array(
'DELETE_USER_POSTS' => 'Delete pruned user posts', 'DELETE_USER_POSTS' => 'Delete pruned user posts',
'DELETE_USER_POSTS_EXPLAIN' => 'Removes posts made by deleted users, has no effect if users are deactivated.', 'DELETE_USER_POSTS_EXPLAIN' => 'Removes posts made by deleted users, has no effect if users are deactivated.',
'JOINED_EXPLAIN' => 'Enter a date in <kbd>YYYY-MM-DD</kbd> format.', 'JOINED_EXPLAIN' => 'Enter a date in <kbd>YYYY-MM-DD</kbd> format. You may use both fields to specify an interval, or leave one blank for an open date range.',
'LAST_ACTIVE_EXPLAIN' => 'Enter a date in <kbd>YYYY-MM-DD</kbd> format. Enter <kbd>0000-00-00</kbd> to prune users who never logged in, <em>Before</em> and <em>After</em> conditions will be ignored.', 'LAST_ACTIVE_EXPLAIN' => 'Enter a date in <kbd>YYYY-MM-DD</kbd> format. Enter <kbd>0000-00-00</kbd> to prune users who never logged in, <em>Before</em> and <em>After</em> conditions will be ignored.',
'POSTS_ON_QUEUE' => 'Posts Awaiting Approval',
'PRUNE_USERS_GROUP_EXPLAIN' => 'Selects all members of the group for pruning.',
'PRUNE_USERS_LIST' => 'Users to be pruned', 'PRUNE_USERS_LIST' => 'Users to be pruned',
'PRUNE_USERS_LIST_DELETE' => 'With the selected critera for pruning users the following accounts will be removed.', 'PRUNE_USERS_LIST_DELETE' => 'With the selected critera for pruning users the following accounts will be removed. You can remove individual users from the deletion list by unchecking the box next to their username.',
'PRUNE_USERS_LIST_DEACTIVATE' => 'With the selected critera for pruning users the following accounts will be deactivated.', 'PRUNE_USERS_LIST_DEACTIVATE' => 'With the selected critera for pruning users the following accounts will be deactivated. You can remove individual users from the deactivation list by unchecking the box next to their username.',
'SELECT_USERS_EXPLAIN' => 'Enter specific usernames here, they will be used in preference to the criteria above. Founders cannot be pruned.', 'SELECT_USERS_EXPLAIN' => 'Enter specific usernames here. They will be used in preference to the criteria above. Founders cannot be pruned.',
'USER_DEACTIVATE_SUCCESS' => 'The selected users have been deactivated successfully.', 'USER_DEACTIVATE_SUCCESS' => 'The selected users have been deactivated successfully.',
'USER_DELETE_SUCCESS' => 'The selected users have been deleted successfully.', 'USER_DELETE_SUCCESS' => 'The selected users have been deleted successfully.',