Skip to content

Instantly share code, notes, and snippets.

@joshuaadickerson
Created February 15, 2014 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshuaadickerson/9025761 to your computer and use it in GitHub Desktop.
Save joshuaadickerson/9025761 to your computer and use it in GitHub Desktop.
<?php
function adjustBoardCounts(array $boards)
{
$db = database();
foreach ($boards as $id_board => $stats)
{
if (function_exists('apache_reset_timeout'))
@apache_reset_timeout();
$db->query('', '
UPDATE {db_prefix}boards
SET
num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END,
num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END,
unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END,
unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END
WHERE id_board = {int:id_board}',
array(
'id_board' => $id_board,
'num_posts' => $stats['num_posts'],
'num_topics' => $stats['num_topics'],
'unapproved_posts' => $stats['unapproved_posts'],
'unapproved_topics' => $stats['unapproved_topics'],
)
);
}
}
<?php
function removeEventsByTopics(array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}calendar
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
function clearLikesByTopics (array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}message_likes
INNER JOIN {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
function clearMentionsByTopics (array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}log_mentions
INNER JOIN {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
function recycleMessagesByTopic(array $topics)
{
$db = database();
// Mark recycled messages as recycled.
$db->query('', '
UPDATE {db_prefix}messages
SET icon = {string:recycled}
WHERE id_topic IN ({array_int:recycle_topics})',
array(
'recycle_topics' => $topics,
'recycled' => 'recycled',
)
);
}
function removeMessagesByTopics (array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
function closeReportsByTopic(array $topics)
{
$db = database();
$db->query('', '
UPDATE {db_prefix}log_reported
SET closed = {int:is_closed}
WHERE id_topic IN ({array_int:recycle_topics})',
array(
'recycle_topics' => $topics,
'is_closed' => 1,
)
);
}
<?php
function clearNotificationsByTopics(array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}log_notify
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
/**
* Removes the passed id_topic's.
* Permissions are NOT checked here because the function is used in a scheduled task
*
* @param int[]|int $topic_ids The topics to remove (can be an id or an array of ids).
* @param bool $decreasePostCount if true users' post count will be reduced
* @param bool $ignoreRecycling if true topics are not moved to the recycle board (if it exists).
*/
function removeTopics($topic_ids, $decreasePostCount = true, $ignoreRecycling = false)
{
global $modSettings;
$db = database();
// Nothing to do?
if (empty($topic_ids))
return;
// Only a single topic.
if (is_numeric($topic_ids))
$topic_ids = array($topic_ids);
$do_recycle = !$ignoreRecycling && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0;
$request = $db->query('', '
SELECT id_topic, id_board, approved, unapproved_posts, id_poll, num_replies, b.count_posts
FROM {db_prefix}topics
LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topic_ids,
)
);
$topics = array();
$polls = array();
$boards = array();
$recycle_topics = array();
while ($row = $db->fetch_assoc($request))
{
$topics[$row['id_topic']] = $row + array('count' => (bool) $row['count_posts']);
// All of the board info
if (!isset($boards[$row['id_board']]))
{
$boards[$row['id_board']] = array(
'unapproved_posts' => 0,
'approved_topics' => 0,
'unapproved_topics' => 0,
'num_posts' => 0,
'num_topics' => 0,
'count_posts' => (bool) $row['count_posts'],
);
}
$boards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
$boards[$row['id_board']]['posts'] += $row['num_replies'] + 1;
$boards[$row['id_board']]['num_topics']++;
if ((bool) $row['approved'])
{
$boards[$row['id_board']]['approved_topics']++;
}
else
{
$boards[$row['id_board']]['unapproved_topics']++;
}
// These topics will be recycled
if ($do_recycle && $row['id_board'] != $modSettings['recycle_board'])
{
$recycle_topics[] = $row['id_board'];
}
// Get all of the polls
if (!empty($row['id_poll']))
{
$polls[] = $row['id_poll'];
}
}
// Just in case none of the topics were found
if (empty($topics))
{
// @todo throw an exception?
return;
}
// @todo move to Members.subs.php -> adjustPostCount()
// Decrease the post counts for members.
if ($decreasePostCount)
{
$messages = anotherGetTopicFunction($topic_ids, $members);
$members = getMemberAdjustmentsFromMessageInfo($messages, $boards);
foreach ($members as $id_mem => $member)
{
}
if ($db->num_rows($requestMembers) > 0)
{
while ($rowMembers = $db->fetch_assoc($requestMembers))
updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $member['approved_posts']));
}
}
// Recycle topics that aren't in the recycle board...
if ($do_recycle && !empty($recycle_topics))
{
recycleTopics($recycle_topics);
// Still topics left to delete?
if (count($recycle_topics) === count($topics))
return;
}
// Decrease number of posts and topics for each board.
require_once(SUBSDIR . '/Boards.subs.php');
adjustBoardCounts($boards);
// Remove the polls
if (!empty($polls))
{
require_once(SUBSDIR . '/Polls.subs.php');
removePoll($polls);
}
// Get rid of the attachment(s).
require_once(SUBSDIR . '/ManageAttachments.subs.php');
$attachmentQuery = array(
// @todo can't this not have the attachment_type and still work?
'attachment_type' => 0,
'id_topic' => $topic_ids,
);
removeAttachments($attachmentQuery, 'messages');
// Delete search index entries.
if (!empty($modSettings['search_custom_index_config']))
{
require_once(SUBSDIR . '/Search.subs.php');
removeSearchLogByTopic($topic_ids);
}
// Remove all likes now that the topic is gone
require_once(SUBSDIR . '/Likes.subs.php');
clearLikesByTopics($topic_ids);
// Remove all mentions now that the topic is gone
require_once(SUBSDIR . '/Mentions.subs.php');
clearMentionsByTopics($topic_ids);
// Delete messages in each topic.
require_once(SUBSDIR . '/Messages.subs.php');
removeMessagesByTopics($topic_ids);
// Remove linked calendar events.
require_once(SUBSDIR . '/Calendar.subs.php');
clearEventsByTopic($topic_ids);
// Delete log_topics data
clearTopicLog(null, $topic_ids);
// Delete notifications
require_once(SUBSDIR . '/Notifications.subs.php');
clearNotificationsByTopics($topic_ids);
// Remove data from the subjects for search cache
require_once(SUBSDIR . '/Search.subs.php');
removeSearchLogByTopics($topic_ids);
require_once(SUBSDIR . '/FollowUps.subs.php');
removeFollowUpsByTopic($topics);
// Delete the topics themselves
$db->query('', '
DELETE FROM {db_prefix}topics
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topic_ids,
)
);
// Maybe there's an addon that wants to delete topic related data of its own
call_integration_hook('integrate_remove_topics', array(
'topics' => $topic_ids,
//'messages' => $messages,
//'members' => $members,
'boards' => $boards,
'polls' => $polls
));
// Update the totals...
updateStats('message');
updateTopicStats();
// @todo move to the calendar or controller
updateSettings(array(
'calendar_updated' => time(),
));
require_once(SUBSDIR . '/Post.subs.php');
updateLastMessages(array_keys($boards));
}
function recycleTopics(array $topics)
{
global $modSettings;
$db = database();
foreach ($topics as $id => $topic)
{
if (function_exists('apache_reset_timeout'))
@apache_reset_timeout();
// @todo this should be done by moveTopics() makes no sense to do an update on topics twice
// Set the id_previous_board for this topic - and make it not sticky.
$db->query('', '
UPDATE {db_prefix}topics
SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky}
WHERE id_topic = {int:id_topic}',
array(
'id_previous_board' => $topic['id_board'],
'id_topic' => $id,
'not_sticky' => 0,
)
);
}
require_once(SUBSDIR . '/Messages.subs.php');
recycleMessagesByTopics($topics);
// Move the topics to the recycle board.
require_once(SUBSDIR . '/Topic.subs.php');
moveTopics($topics, $modSettings['recycle_board']);
// Close reports that are being recycled.
require_once(SUBSDIR . '/Moderation.subs.php');
closeReportsByTopic($topics);
updateSettings(array('last_mod_report_action' => time()));
recountOpenReports();
}
function removeSearchWordsByTopics($topics)
{
global $modSettings;
$db = database();
$customIndexSettings = unserialize($modSettings['search_custom_index_config']);
$words = array();
$messages = array();
// @todo Pretty sure this isn't needed. We're deleting the entire topic, so why worry about individual words?
// Just delete all of the messages, right?
$request = $db->query('', '
SELECT id_msg, body
FROM {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
while ($row = $db->fetch_assoc($request))
{
if (function_exists('apache_reset_timeout'))
@apache_reset_timeout();
$words = array_merge($words, text2words($row['body'], $customIndexSettings['bytes_per_word'], true));
$messages[] = $row['id_msg'];
}
$db->free_result($request);
$words = array_unique($words);
if (!empty($words) && !empty($messages))
{
// @todo I think that id_word IN() isn't needed, so I removed it.
$db->query('', '
DELETE FROM {db_prefix}log_search_words
WHERE id_msg IN ({array_int:message_list})',
array(
'word_list' => $words,
'message_list' => $messages,
)
);
}
}
/**
*
* @param array $messages
*/
function removeSearchLogByTopics(array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}log_search_words
INNER JOIN {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
/**
*
* @param array $topics
* @param array $members if you pass members, it will return some member information
* @return type
*/
function anotherGetTopicMessagesFunction(array $topics, array &$members = array())
{
$db = database();
$messages = array();
$request = $db->query('', '
SELECT id_topic, id_msg, id_member, icon, approved
FROM {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
while ($row = $db->fetch_assoc($request))
{
$messages[$row['id_msg']] = $row;
}
return $messages;
}
function getMemberAdjustmentsFromMessageInfo(array $messages, array $boards)
{
$members = array();
foreach ($messages as $message)
{
$id_mem = $message['id_member'];
if (!isset($members[$id_mem]))
{
$members[$id_mem] = array(
'messages' => 0,
'approved_messages' => 0,
'unapproved_messages' => 0,
'adjustment' => 0,
);
}
$members[$id_mem] = array(
'messages' => $members[$id_mem]['messages'] + 1,
'approved_messages' => (bool) $message['approved'] ? $members[$id_mem]['approved_messages'] + 1 : $members[$id_mem]['approved_messages'],
'unapproved_messages' => (bool) !$message['approved'] ? $members[$id_mem]['unapproved_messages'] + 1 : $members[$id_mem]['unapproved_messages'],
);
}
return $members;
}
// @todo move to Boards.subs.php
function adjustBoardCounts(array $boards)
{
$db = database();
foreach ($boards as $id_board => $stats)
{
if (function_exists('apache_reset_timeout'))
@apache_reset_timeout();
$db->query('', '
UPDATE {db_prefix}boards
SET
num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END,
num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END,
unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END,
unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END
WHERE id_board = {int:id_board}',
array(
'id_board' => $id_board,
'num_posts' => $stats['num_posts'],
'num_topics' => $stats['num_topics'],
'unapproved_posts' => $stats['unapproved_posts'],
'unapproved_topics' => $stats['unapproved_topics'],
)
);
}
}
function clearTopicLog(array $members = null, array $topics = null)
{
$db = database();
$sql = $members === null ? 1 : 'id_member IN ({array_int:members}) AND ';
$sql .= $topics === null ? 1 : 'id_topic IN ({array_int:topics})';
$db->query('', '
DELETE FROM {db_prefix}log_topics
WHERE ' . $sql,
array(
'members' => $members,
'topics' => $topics,
)
);
}
function recycleMessagesByTopic(array $topics)
{
$db = database();
// Mark recycled messages as recycled.
$db->query('', '
UPDATE {db_prefix}messages
SET icon = {string:recycled}
WHERE id_topic IN ({array_int:recycle_topics})',
array(
'recycle_topics' => $topics,
'recycled' => 'recycled',
)
);
}
function closeReportsByTopic(array $topics)
{
$db = database();
$db->query('', '
UPDATE {db_prefix}log_reported
SET closed = {int:is_closed}
WHERE id_topic IN ({array_int:recycle_topics})',
array(
'recycle_topics' => $topics,
'is_closed' => 1,
)
);
}
function clearNotificationsByTopics(array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}log_notify
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
function removeEventsByTopics(array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}calendar
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
function clearLikesByTopics (array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}message_likes
INNER JOIN {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
function clearMentionsByTopics (array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}log_mentions
INNER JOIN {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
function removeMessagesByTopics (array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
function removeSearchLogByTopics(array $topics)
{
$db = database();
$db->query('', '
DELETE FROM {db_prefix}log_search_words
INNER JOIN {db_prefix}messages
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topics,
)
);
}
<?php
/**
* Removes the passed id_topic's.
* Permissions are NOT checked here because the function is used in a scheduled task
*
* @param int[]|int $topic_ids The topics to remove (can be an id or an array of ids).
* @param bool $decreasePostCount if true users' post count will be reduced
* @param bool $ignoreRecycling if true topics are not moved to the recycle board (if it exists).
*/
function removeTopics($topic_ids, $decreasePostCount = true, $ignoreRecycling = false)
{
global $modSettings;
$db = database();
// Nothing to do?
if (empty($topic_ids))
return;
// Only a single topic.
if (is_numeric($topic_ids))
$topic_ids = array($topic_ids);
$do_recycle = !$ignoreRecycling && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0;
$request = $db->query('', '
SELECT id_topic, id_board, approved, unapproved_posts, id_poll, num_replies, b.count_posts
FROM {db_prefix}topics
LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topic_ids,
)
);
$topics = array();
$polls = array();
$boards = array();
$recycle_topics = array();
while ($row = $db->fetch_assoc($request))
{
$topics[$row['id_topic']] = $row + array('count' => (bool) $row['count_posts']);
// All of the board info
if (!isset($boards[$row['id_board']]))
{
$boards[$row['id_board']] = array(
'unapproved_posts' => 0,
'approved_topics' => 0,
'unapproved_topics' => 0,
'num_posts' => 0,
'num_topics' => 0,
'count_posts' => (bool) $row['count_posts'],
);
}
$boards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
$boards[$row['id_board']]['posts'] += $row['num_replies'] + 1;
$boards[$row['id_board']]['num_topics']++;
if ((bool) $row['approved'])
{
$boards[$row['id_board']]['approved_topics']++;
}
else
{
$boards[$row['id_board']]['unapproved_topics']++;
}
// These topics will be recycled
if ($do_recycle && $row['id_board'] != $modSettings['recycle_board'])
{
$recycle_topics[] = $row['id_board'];
}
// Get all of the polls
if (!empty($row['id_poll']))
{
$polls[] = $row['id_poll'];
}
}
// Just in case none of the topics were found
if (empty($topics))
{
// @todo throw an exception?
return;
}
// @todo move to Members.subs.php -> adjustPostCount()
// Decrease the post counts for members.
if ($decreasePostCount)
{
$messages = anotherGetTopicFunction($topic_ids, $members);
$members = getMemberAdjustmentsFromMessageInfo($messages, $boards);
foreach ($members as $id_mem => $member)
{
}
if ($db->num_rows($requestMembers) > 0)
{
while ($rowMembers = $db->fetch_assoc($requestMembers))
updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $member['approved_posts']));
}
}
// Recycle topics that aren't in the recycle board...
if ($do_recycle && !empty($recycle_topics))
{
recycleTopics($recycle_topics);
// Still topics left to delete?
if (count($recycle_topics) === count($topics))
return;
}
// Decrease number of posts and topics for each board.
require_once(SUBSDIR . '/Boards.subs.php');
adjustBoardCounts($boards);
// Remove the polls
if (!empty($polls))
{
require_once(SUBSDIR . '/Polls.subs.php');
removePoll($polls);
}
// Get rid of the attachment(s).
require_once(SUBSDIR . '/ManageAttachments.subs.php');
$attachmentQuery = array(
// @todo can't this not have the attachment_type and still work?
'attachment_type' => 0,
'id_topic' => $topic_ids,
);
removeAttachments($attachmentQuery, 'messages');
// Delete search index entries.
if (!empty($modSettings['search_custom_index_config']))
{
require_once(SUBSDIR . '/Search.subs.php');
removeSearchLogByTopic($topic_ids);
}
// Remove all likes now that the topic is gone
require_once(SUBSDIR . '/Likes.subs.php');
clearLikesByTopics($topic_ids);
// Remove all mentions now that the topic is gone
require_once(SUBSDIR . '/Mentions.subs.php');
clearMentionsByTopics($topic_ids);
// Delete messages in each topic.
require_once(SUBSDIR . '/Messages.subs.php');
removeMessagesByTopics($topic_ids);
// Remove linked calendar events.
require_once(SUBSDIR . '/Calendar.subs.php');
clearEventsByTopic($topic_ids);
// Delete log_topics data
clearTopicLog(null, $topic_ids);
// Delete notifications
require_once(SUBSDIR . '/Notifications.subs.php');
clearNotificationsByTopics($topic_ids);
// Remove data from the subjects for search cache
require_once(SUBSDIR . '/Search.subs.php');
removeSearchLogByTopics($topic_ids);
require_once(SUBSDIR . '/FollowUps.subs.php');
removeFollowUpsByTopic($topics);
// Delete the topics themselves
$db->query('', '
DELETE FROM {db_prefix}topics
WHERE id_topic IN ({array_int:topics})',
array(
'topics' => $topic_ids,
)
);
// Maybe there's an addon that wants to delete topic related data of its own
call_integration_hook('integrate_remove_topics', array(
'topics' => $topic_ids,
//'messages' => $messages,
//'members' => $members,
'boards' => $boards,
'polls' => $polls
));
// Update the totals...
updateStats('message');
updateTopicStats();
// @todo move to the calendar or controller
updateSettings(array(
'calendar_updated' => time(),
));
require_once(SUBSDIR . '/Post.subs.php');
updateLastMessages(array_keys($boards));
}
function clearTopicLog(array $members = null, array $topics = null)
{
$db = database();
$sql = $members === null ? 1 : 'id_member IN ({array_int:members}) AND ';
$sql .= $topics === null ? 1 : 'id_topic IN ({array_int:topics})';
$db->query('', '
DELETE FROM {db_prefix}log_topics
WHERE ' . $sql,
array(
'members' => $members,
'topics' => $topics,
)
);
}
function recycleTopics(array $topics)
{
global $modSettings;
$db = database();
foreach ($topics as $id => $topic)
{
if (function_exists('apache_reset_timeout'))
@apache_reset_timeout();
// @todo this should be done by moveTopics() makes no sense to do an update on topics twice
// Set the id_previous_board for this topic - and make it not sticky.
$db->query('', '
UPDATE {db_prefix}topics
SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky}
WHERE id_topic = {int:id_topic}',
array(
'id_previous_board' => $topic['id_board'],
'id_topic' => $id,
'not_sticky' => 0,
)
);
}
require_once(SUBSDIR . '/Messages.subs.php');
recycleMessagesByTopics($topics);
// Move the topics to the recycle board.
require_once(SUBSDIR . '/Topic.subs.php');
moveTopics($topics, $modSettings['recycle_board']);
// Close reports that are being recycled.
require_once(SUBSDIR . '/Moderation.subs.php');
closeReportsByTopic($topics);
updateSettings(array('last_mod_report_action' => time()));
recountOpenReports();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment