function logAction($action, $extra = array(), $log_type = 'moderate') { global $modSettings, $user_info, $smcFunc, $sourcedir; $log_types = array('moderate' => 1, 'user' => 2, 'admin' => 3); if (!is_array($extra)) { trigger_error('logAction(): data is not an array with action \'' . $action . '\'', E_USER_NOTICE); } // Pull out the parts we want to store separately, but also make sure that the data is proper if (isset($extra['topic'])) { if (!is_numeric($extra['topic'])) { trigger_error('logAction(): data\'s topic is not a number', E_USER_NOTICE); } $topic_id = empty($extra['topic']) ? '0' : (int) $extra['topic']; unset($extra['topic']); } else { $topic_id = '0'; } if (isset($extra['message'])) { if (!is_numeric($extra['message'])) { trigger_error('logAction(): data\'s message is not a number', E_USER_NOTICE); } $msg_id = empty($extra['message']) ? '0' : (int) $extra['message']; unset($extra['message']); } else { $msg_id = '0'; } // Is there an associated report on this? if (in_array($action, array('move', 'remove', 'split', 'merge'))) { $request = $smcFunc['db_query']('', ' SELECT id_report FROM {db_prefix}log_reported WHERE {raw:column_name} = {int:reported} LIMIT 1', array('column_name' => !empty($msg_id) ? 'id_msg' : 'id_topic', 'reported' => !empty($msg_id) ? $msg_id : $topic_id)); // Alright, if we get any result back, update open reports. if ($smcFunc['db_num_rows']($request) > 0) { require_once $sourcedir . '/ModerationCenter.php'; updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } $smcFunc['db_free_result']($request); } // No point in doing anything else, if the log isn't even enabled. if (empty($modSettings['modlog_enabled']) || !isset($log_types[$log_type])) { return false; } if (isset($extra['member']) && !is_numeric($extra['member'])) { trigger_error('logAction(): data\'s member is not a number', E_USER_NOTICE); } if (isset($extra['board'])) { if (!is_numeric($extra['board'])) { trigger_error('logAction(): data\'s board is not a number', E_USER_NOTICE); } $board_id = empty($extra['board']) ? '0' : (int) $extra['board']; unset($extra['board']); } else { $board_id = '0'; } if (isset($extra['board_to'])) { if (!is_numeric($extra['board_to'])) { trigger_error('logAction(): data\'s board_to is not a number', E_USER_NOTICE); } if (empty($board_id)) { $board_id = empty($extra['board_to']) ? '0' : (int) $extra['board_to']; unset($extra['board_to']); } } $smcFunc['db_insert']('', '{db_prefix}log_actions', array('log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534'), array(time(), $log_types[$log_type], $user_info['id'], $user_info['ip'], $action, $board_id, $topic_id, $msg_id, serialize($extra)), array('id_action')); return $smcFunc['db_insert_id']('{db_prefix}log_actions', 'id_action'); }
function ReportedPosts() { global $txt, $context, $scripturl, $modSettings, $user_info, $smcFunc; loadTemplate('ModerationCenter'); // Put the open and closed options into tabs, because we can... $context[$context['moderation_menu_name']]['tab_data'] = array('title' => $txt['mc_reported_posts'], 'help' => '', 'description' => $txt['mc_reported_posts_desc']); // This comes under the umbrella of moderating posts. if ($user_info['mod_cache']['bq'] == '0=1') { isAllowedTo('moderate_forum'); } // Are they wanting to view a particular report? if (!empty($_REQUEST['report'])) { return ModReport(); } // Set up the comforting bits... $context['page_title'] = $txt['mc_reported_posts']; $context['sub_template'] = 'reported_posts'; // Are we viewing open or closed reports? $context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0; // Are we doing any work? if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid'])) { checkSession('get'); $_GET['rid'] = (int) $_GET['rid']; // Update the report... $smcFunc['db_query']('', ' UPDATE {db_prefix}log_reported SET ' . (isset($_GET['ignore']) ? 'ignore_all = {int:ignore_all}' : 'closed = {int:closed}') . ' WHERE id_report = {int:id_report} AND ' . $user_info['mod_cache']['bq'], array('ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0, 'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0, 'id_report' => $_GET['rid'])); // Time to update. updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } elseif (isset($_POST['close']) && isset($_POST['close_selected'])) { checkSession('post'); // All the ones to update... $toClose = array(); foreach ($_POST['close'] as $rid) { $toClose[] = (int) $rid; } if (!empty($toClose)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}log_reported SET closed = {int:is_closed} WHERE id_report IN ({array_int:report_list}) AND ' . $user_info['mod_cache']['bq'], array('report_list' => $toClose, 'is_closed' => 1)); // Time to update. updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } } // How many entries are we viewing? $request = $smcFunc['db_query']('', ' SELECT COUNT(*) FROM {db_prefix}log_reported AS lr WHERE lr.closed = {int:view_closed} AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']), array('view_closed' => $context['view_closed'])); list($context['total_reports']) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); // So, that means we can page index, yes? $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reports' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10); $context['start'] = $_GET['start']; // By George, that means we in a position to get the reports, golly good. $request = $smcFunc['db_query']('', ' SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body, lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all, IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author FROM {db_prefix}log_reported AS lr LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member) WHERE lr.closed = {int:view_closed} AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . ' ORDER BY lr.time_updated DESC LIMIT ' . $context['start'] . ', 10', array('view_closed' => $context['view_closed'])); $context['reports'] = array(); $report_ids = array(); for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++) { $report_ids[] = $row['id_report']; $context['reports'][$row['id_report']] = array('id' => $row['id_report'], 'alternate' => $i % 2, 'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], 'author' => array('id' => $row['id_author'], 'name' => $row['author_name'], 'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'], 'href' => $scripturl . '?action=profile;u=' . $row['id_author']), 'comments' => array(), 'time_started' => timeformat($row['time_started']), 'last_updated' => timeformat($row['time_updated']), 'subject' => $row['subject'], 'body' => parse_bbc($row['body']), 'num_reports' => $row['num_reports'], 'closed' => $row['closed'], 'ignore' => $row['ignore_all']); } $smcFunc['db_free_result']($request); // Now get all the people who reported it. if (!empty($report_ids)) { $request = $smcFunc['db_query']('', ' SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment, IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter FROM {db_prefix}log_reported_comments AS lrc LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member) WHERE lrc.id_report IN ({array_int:report_list})', array('report_list' => $report_ids)); while ($row = $smcFunc['db_fetch_assoc']($request)) { $context['reports'][$row['id_report']]['comments'][] = array('id' => $row['id_comment'], 'message' => $row['comment'], 'time' => timeformat($row['time_sent']), 'member' => array('id' => $row['id_member'], 'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'], 'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']), 'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '')); } $smcFunc['db_free_result']($request); } }
function banPermissions() { global $user_info, $sourcedir, $modSettings, $context; // Somehow they got here, at least take away all permissions... if (isset($_SESSION['ban']['cannot_access'])) { $user_info['permissions'] = array(); } elseif (isset($_SESSION['ban']['cannot_post']) || !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $user_info['warning']) { $denied_permissions = array('pm_send', 'calendar_post', 'calendar_edit_own', 'calendar_edit_any', 'poll_post', 'poll_add_own', 'poll_add_any', 'poll_edit_own', 'poll_edit_any', 'poll_lock_own', 'poll_lock_any', 'poll_remove_own', 'poll_remove_any', 'manage_attachments', 'manage_smileys', 'manage_boards', 'admin_forum', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'profile_identity_any', 'profile_extra_any', 'profile_title_any', 'post_new', 'post_reply_own', 'post_reply_any', 'delete_own', 'delete_any', 'delete_replies', 'make_sticky', 'merge_any', 'split_any', 'modify_own', 'modify_any', 'modify_replies', 'move_any', 'send_topic', 'lock_own', 'lock_any', 'remove_own', 'remove_any', 'post_unapproved_topics', 'post_unapproved_replies_own', 'post_unapproved_replies_any'); $user_info['permissions'] = array_diff($user_info['permissions'], $denied_permissions); } elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $user_info['warning']) { // Work out what permissions should change... $permission_change = array('post_new' => 'post_unapproved_topics', 'post_reply_own' => 'post_unapproved_replies_own', 'post_reply_any' => 'post_unapproved_replies_any', 'post_attachment' => 'post_unapproved_attachments'); foreach ($permission_change as $old => $new) { if (!in_array($old, $user_info['permissions'])) { unset($permission_change[$old]); } else { $user_info['permissions'][] = $new; } } $user_info['permissions'] = array_diff($user_info['permissions'], array_keys($permission_change)); } //!!! Find a better place to call this? Needs to be after permissions loaded! // Finally, some bits we cache in the session because it saves queries. if (isset($_SESSION['mc']) && $_SESSION['mc']['time'] > $modSettings['settings_updated'] && $_SESSION['mc']['id'] == $user_info['id']) { $user_info['mod_cache'] = $_SESSION['mc']; } else { require_once $sourcedir . '/Subs-Auth.php'; rebuildModCache(); } // Now that we have the mod cache taken care of lets setup a cache for the number of mod reports still open if (isset($_SESSION['rc']) && $_SESSION['rc']['time'] > $modSettings['last_mod_report_action'] && $_SESSION['rc']['id'] == $user_info['id']) { $context['open_mod_reports'] = $_SESSION['rc']['reports']; } elseif ($_SESSION['mc']['bq'] != '0=1') { require_once $sourcedir . '/ModerationCenter.php'; recountOpenReports(); } else { $context['open_mod_reports'] = 0; } }
/** * Remove a specific message (including permission checks). * - normally, local and global should be the localCookies and globalCookies settings, respectively. * - uses boardurl to determine these two things. * * @param int $message The message id * @param bool $decreasePostCount if true users' post count will be reduced * @return array an array to set the cookie on with domain and path in it, in that order */ function removeMessage($message, $decreasePostCount = true) { global $board, $sourcedir, $modSettings, $user_info, $smcFunc, $context; if (empty($message) || !is_numeric($message)) { return false; } $request = $smcFunc['db_query']('', ' SELECT m.id_member, m.icon, m.poster_time, m.subject,' . (empty($modSettings['search_custom_index_config']) ? '' : ' m.body,') . ' m.approved, t.id_topic, t.id_first_msg, t.id_last_msg, t.num_replies, t.id_board, t.id_member_started AS id_member_poster, b.count_posts FROM {db_prefix}messages AS m INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) WHERE m.id_msg = {int:id_msg} LIMIT 1', array('id_msg' => $message)); if ($smcFunc['db_num_rows']($request) == 0) { return false; } $row = $smcFunc['db_fetch_assoc']($request); $smcFunc['db_free_result']($request); if (empty($board) || $row['id_board'] != $board) { $delete_any = boardsAllowedTo('delete_any'); if (!in_array(0, $delete_any) && !in_array($row['id_board'], $delete_any)) { $delete_own = boardsAllowedTo('delete_own'); $delete_own = in_array(0, $delete_own) || in_array($row['id_board'], $delete_own); $delete_replies = boardsAllowedTo('delete_replies'); $delete_replies = in_array(0, $delete_replies) || in_array($row['id_board'], $delete_replies); if ($row['id_member'] == $user_info['id']) { if (!$delete_own) { if ($row['id_member_poster'] == $user_info['id']) { if (!$delete_replies) { fatal_lang_error('cannot_delete_replies', 'permission'); } } else { fatal_lang_error('cannot_delete_own', 'permission'); } } elseif (($row['id_member_poster'] != $user_info['id'] || !$delete_replies) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) { fatal_lang_error('modify_post_time_passed', false); } } elseif ($row['id_member_poster'] == $user_info['id']) { if (!$delete_replies) { fatal_lang_error('cannot_delete_replies', 'permission'); } } else { fatal_lang_error('cannot_delete_any', 'permission'); } } // Can't delete an unapproved message, if you can't see it! if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !(in_array(0, $delete_any) || in_array($row['id_board'], $delete_any))) { $approve_posts = boardsAllowedTo('approve_posts'); if (!in_array(0, $approve_posts) && !in_array($row['id_board'], $approve_posts)) { return false; } } } else { // Check permissions to delete this message. if ($row['id_member'] == $user_info['id']) { if (!allowedTo('delete_own')) { if ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any')) { isAllowedTo('delete_replies'); } elseif (!allowedTo('delete_any')) { isAllowedTo('delete_own'); } } elseif (!allowedTo('delete_any') && ($row['id_member_poster'] != $user_info['id'] || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) { fatal_lang_error('modify_post_time_passed', false); } } elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any')) { isAllowedTo('delete_replies'); } else { isAllowedTo('delete_any'); } if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !allowedTo('delete_own')) { isAllowedTo('approve_posts'); } } // Close any moderation reports for this message. $smcFunc['db_query']('', ' UPDATE {db_prefix}log_reported SET closed = {int:is_closed} WHERE id_msg = {int:id_msg}', array('is_closed' => 1, 'id_msg' => $message)); if ($smcFunc['db_affected_rows']() != 0) { require_once $sourcedir . '/ModerationCenter.php'; updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } // Delete the *whole* topic, but only if the topic consists of one message. if ($row['id_first_msg'] == $message) { if (empty($board) || $row['id_board'] != $board) { $remove_any = boardsAllowedTo('remove_any'); $remove_any = in_array(0, $remove_any) || in_array($row['id_board'], $remove_any); if (!$remove_any) { $remove_own = boardsAllowedTo('remove_own'); $remove_own = in_array(0, $remove_own) || in_array($row['id_board'], $remove_own); } if ($row['id_member'] != $user_info['id'] && !$remove_any) { fatal_lang_error('cannot_remove_any', 'permission'); } elseif (!$remove_any && !$remove_own) { fatal_lang_error('cannot_remove_own', 'permission'); } } else { // Check permissions to delete a whole topic. if ($row['id_member'] != $user_info['id']) { isAllowedTo('remove_any'); } elseif (!allowedTo('remove_any')) { isAllowedTo('remove_own'); } } // ...if there is only one post. if (!empty($row['num_replies'])) { fatal_lang_error('delFirstPost', false); } removeTopics($row['id_topic']); return true; } // Deleting a recycled message can not lower anyone's post count. if ($row['icon'] == 'recycled') { $decreasePostCount = false; } // This is the last post, update the last post on the board. if ($row['id_last_msg'] == $message) { // Find the last message, set it, and decrease the post count. $request = $smcFunc['db_query']('', ' SELECT id_msg, id_member FROM {db_prefix}messages WHERE id_topic = {int:id_topic} AND id_msg != {int:id_msg} ORDER BY ' . ($modSettings['postmod_active'] ? 'approved DESC, ' : '') . 'id_msg DESC LIMIT 1', array('id_topic' => $row['id_topic'], 'id_msg' => $message)); $row2 = $smcFunc['db_fetch_assoc']($request); $smcFunc['db_free_result']($request); $smcFunc['db_query']('', ' UPDATE {db_prefix}topics SET id_last_msg = {int:id_last_msg}, id_member_updated = {int:id_member_updated}' . (!$modSettings['postmod_active'] || $row['approved'] ? ', num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : ', unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . ' WHERE id_topic = {int:id_topic}', array('id_last_msg' => $row2['id_msg'], 'id_member_updated' => $row2['id_member'], 'no_replies' => 0, 'no_unapproved' => 0, 'id_topic' => $row['id_topic'])); } else { $smcFunc['db_query']('', ' UPDATE {db_prefix}topics SET ' . ($row['approved'] ? ' num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : ' unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . ' WHERE id_topic = {int:id_topic}', array('no_replies' => 0, 'no_unapproved' => 0, 'id_topic' => $row['id_topic'])); } // Default recycle to false. $recycle = false; // If recycle topics has been set, make a copy of this message in the recycle board. // Make sure we're not recycling messages that are already on the recycle board. if (!empty($modSettings['recycle_enable']) && $row['id_board'] != $modSettings['recycle_board'] && $row['icon'] != 'recycled') { // Check if the recycle board exists and if so get the read status. $request = $smcFunc['db_query']('', ' SELECT (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_seen, id_last_msg FROM {db_prefix}boards AS b LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member}) WHERE b.id_board = {int:recycle_board}', array('current_member' => $user_info['id'], 'recycle_board' => $modSettings['recycle_board'])); if ($smcFunc['db_num_rows']($request) == 0) { fatal_lang_error('recycle_no_valid_board'); } list($isRead, $last_board_msg) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); // Is there an existing topic in the recycle board to group this post with? $request = $smcFunc['db_query']('', ' SELECT id_topic, id_first_msg, id_last_msg FROM {db_prefix}topics WHERE id_previous_topic = {int:id_previous_topic} AND id_board = {int:recycle_board}', array('id_previous_topic' => $row['id_topic'], 'recycle_board' => $modSettings['recycle_board'])); list($id_recycle_topic, $first_topic_msg, $last_topic_msg) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); // Insert a new topic in the recycle board if $id_recycle_topic is empty. if (empty($id_recycle_topic)) { $smcFunc['db_insert']('', '{db_prefix}topics', array('id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int', 'id_last_msg' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', 'id_previous_topic' => 'int'), array($modSettings['recycle_board'], $row['id_member'], $row['id_member'], $message, $message, 0, 1, $row['id_topic']), array('id_topic')); } // Capture the ID of the new topic... $topicID = empty($id_recycle_topic) ? $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic') : $id_recycle_topic; // If the topic creation went successful, move the message. if ($topicID > 0) { $smcFunc['db_query']('', ' UPDATE {db_prefix}messages SET id_topic = {int:id_topic}, id_board = {int:recycle_board}, icon = {string:recycled}, approved = {int:is_approved} WHERE id_msg = {int:id_msg}', array('id_topic' => $topicID, 'recycle_board' => $modSettings['recycle_board'], 'id_msg' => $message, 'recycled' => 'recycled', 'is_approved' => 1)); // Take any reported posts with us... $smcFunc['db_query']('', ' UPDATE {db_prefix}log_reported SET id_topic = {int:id_topic}, id_board = {int:recycle_board} WHERE id_msg = {int:id_msg}', array('id_topic' => $topicID, 'recycle_board' => $modSettings['recycle_board'], 'id_msg' => $message)); // Mark recycled topic as read. if (!$user_info['is_guest']) { $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), array($topicID, $user_info['id'], $modSettings['maxMsgID']), array('id_topic', 'id_member')); } // Mark recycle board as seen, if it was marked as seen before. if (!empty($isRead) && !$user_info['is_guest']) { $smcFunc['db_insert']('replace', '{db_prefix}log_boards', array('id_board' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), array($modSettings['recycle_board'], $user_info['id'], $modSettings['maxMsgID']), array('id_board', 'id_member')); } // Add one topic and post to the recycle bin board. $smcFunc['db_query']('', ' UPDATE {db_prefix}boards SET num_topics = num_topics + {int:num_topics_inc}, num_posts = num_posts + 1' . ($message > $last_board_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . ' WHERE id_board = {int:recycle_board}', array('num_topics_inc' => empty($id_recycle_topic) ? 1 : 0, 'recycle_board' => $modSettings['recycle_board'], 'id_merged_msg' => $message)); // Lets increase the num_replies, and the first/last message ID as appropriate. if (!empty($id_recycle_topic)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}topics SET num_replies = num_replies + 1' . ($message > $last_topic_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . ($message < $first_topic_msg ? ', id_first_msg = {int:id_merged_msg}' : '') . ' WHERE id_topic = {int:id_recycle_topic}', array('id_recycle_topic' => $id_recycle_topic, 'id_merged_msg' => $message)); } // Make sure this message isn't getting deleted later on. $recycle = true; // Make sure we update the search subject index. updateStats('subject', $topicID, $row['subject']); } // If it wasn't approved don't keep it in the queue. if (!$row['approved']) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}approval_queue WHERE id_msg = {int:id_msg} AND id_attach = {int:id_attach}', array('id_msg' => $message, 'id_attach' => 0)); } } $smcFunc['db_query']('', ' UPDATE {db_prefix}boards SET ' . ($row['approved'] ? ' num_posts = CASE WHEN num_posts = {int:no_posts} THEN 0 ELSE num_posts - 1 END' : ' unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . ' WHERE id_board = {int:id_board}', array('no_posts' => 0, 'no_unapproved' => 0, 'id_board' => $row['id_board'])); // If the poster was registered and the board this message was on incremented // the member's posts when it was posted, decrease his or her post count. if (!empty($row['id_member']) && $decreasePostCount && empty($row['count_posts']) && $row['approved']) { updateMemberData($row['id_member'], array('posts' => '-')); } // Only remove posts if they're not recycled. if (!$recycle) { // Remove the message! $smcFunc['db_query']('', ' DELETE FROM {db_prefix}messages WHERE id_msg = {int:id_msg}', array('id_msg' => $message)); if (!empty($modSettings['search_custom_index_config'])) { $customIndexSettings = unserialize($modSettings['search_custom_index_config']); $words = text2words($row['body'], $customIndexSettings['bytes_per_word'], true); if (!empty($words)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_search_words WHERE id_word IN ({array_int:word_list}) AND id_msg = {int:id_msg}', array('word_list' => $words, 'id_msg' => $message)); } } // Delete attachment(s) if they exist. require_once $sourcedir . '/ManageAttachments.php'; $attachmentQuery = array('attachment_type' => 0, 'id_msg' => $message); removeAttachments($attachmentQuery); // Allow mods to remove message related data of their own (likes, maybe?) call_integration_hook('integrate_remove_message', array($message)); } // Update the pesky statistics. updateStats('message'); updateStats('topic'); updateSettings(array('calendar_updated' => time())); // And now to update the last message of each board we messed with. require_once $sourcedir . '/Subs-Post.php'; if ($recycle) { updateLastMessages(array($row['id_board'], $modSettings['recycle_board'])); } else { updateLastMessages($row['id_board']); } return false; }
/** * Removes the passed id_topic's. * Permissions are NOT checked here because the function is used in a scheduled task * * @param int[]|int $topics 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($topics, $decreasePostCount = true, $ignoreRecycling = false) { global $modSettings; $db = database(); // Nothing to do? if (empty($topics)) { return; } // Only a single topic. if (is_numeric($topics)) { $topics = array($topics); } // Decrease the post counts for members. if ($decreasePostCount) { $requestMembers = $db->query('', ' SELECT m.id_member, COUNT(*) AS posts FROM {db_prefix}messages AS m INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) WHERE m.id_topic IN ({array_int:topics}) AND m.icon != {string:recycled} AND b.count_posts = {int:do_count_posts} AND m.approved = {int:is_approved} GROUP BY m.id_member', array('do_count_posts' => 0, 'recycled' => 'recycled', 'topics' => $topics, 'is_approved' => 1)); if ($db->num_rows($requestMembers) > 0) { while ($rowMembers = $db->fetch_assoc($requestMembers)) { updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $rowMembers['posts'])); } } $db->free_result($requestMembers); } // Recycle topics that aren't in the recycle board... if (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 && !$ignoreRecycling) { $request = $db->query('', ' SELECT id_topic, id_board, unapproved_posts, approved FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) AND id_board != {int:recycle_board} LIMIT ' . count($topics), array('recycle_board' => $modSettings['recycle_board'], 'topics' => $topics)); if ($db->num_rows($request) > 0) { // Get topics that will be recycled. $recycleTopics = array(); while ($row = $db->fetch_assoc($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $recycleTopics[] = $row['id_topic']; // 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' => $row['id_board'], 'id_topic' => $row['id_topic'], 'not_sticky' => 0)); } $db->free_result($request); // Mark recycled topics as recycled. $db->query('', ' UPDATE {db_prefix}messages SET icon = {string:recycled} WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'recycled' => 'recycled')); // Move the topics to the recycle board. require_once SUBSDIR . '/Topic.subs.php'; moveTopics($recycleTopics, $modSettings['recycle_board']); // Close reports that are being recycled. require_once SUBSDIR . '/Moderation.subs.php'; $db->query('', ' UPDATE {db_prefix}log_reported SET closed = {int:is_closed} WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'is_closed' => 1)); updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); // Topics that were recycled don't need to be deleted, so subtract them. $topics = array_diff($topics, $recycleTopics); } else { $db->free_result($request); } } // Still topics left to delete? if (empty($topics)) { return; } $adjustBoards = array(); // Find out how many posts we are deleting. $request = $db->query('', ' SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts, SUM(num_replies) AS num_replies FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) GROUP BY id_board, approved', array('topics' => $topics)); while ($row = $db->fetch_assoc($request)) { if (!isset($adjustBoards[$row['id_board']]['num_posts'])) { cache_put_data('board-' . $row['id_board'], null, 120); $adjustBoards[$row['id_board']] = array('num_posts' => 0, 'num_topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0, 'id_board' => $row['id_board']); } // Posts = (num_replies + 1) for each approved topic. $adjustBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0); $adjustBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; // Add the topics to the right type. if ($row['approved']) { $adjustBoards[$row['id_board']]['num_topics'] += $row['num_topics']; } else { $adjustBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics']; } } $db->free_result($request); // Decrease number of posts and topics for each board. foreach ($adjustBoards as $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' => $stats['id_board'], 'num_posts' => $stats['num_posts'], 'num_topics' => $stats['num_topics'], 'unapproved_posts' => $stats['unapproved_posts'], 'unapproved_topics' => $stats['unapproved_topics'])); } // Remove polls for these topics. $request = $db->query('', ' SELECT id_poll FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) AND id_poll > {int:no_poll} LIMIT ' . count($topics), array('no_poll' => 0, 'topics' => $topics)); $polls = array(); while ($row = $db->fetch_assoc($request)) { $polls[] = $row['id_poll']; } $db->free_result($request); if (!empty($polls)) { $db->query('', ' DELETE FROM {db_prefix}polls WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); $db->query('', ' DELETE FROM {db_prefix}poll_choices WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); $db->query('', ' DELETE FROM {db_prefix}log_polls WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); } // Get rid of the attachment(s). require_once SUBSDIR . '/ManageAttachments.subs.php'; $attachmentQuery = array('attachment_type' => 0, 'id_topic' => $topics); removeAttachments($attachmentQuery, 'messages'); // Delete search index entries. if (!empty($modSettings['search_custom_index_config'])) { $customIndexSettings = unserialize($modSettings['search_custom_index_config']); $request = $db->query('', ' SELECT id_msg, body FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $words = array(); $messages = array(); 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)) { $db->query('', ' DELETE FROM {db_prefix}log_search_words WHERE id_word IN ({array_int:word_list}) AND id_msg IN ({array_int:message_list})', array('word_list' => $words, 'message_list' => $messages)); } } // Reuse the message array if available if (empty($messages)) { $messages = messagesInTopics($topics); } // If there are messages left in this topic if (!empty($messages)) { // Remove all likes now that the topic is gone $db->query('', ' DELETE FROM {db_prefix}message_likes WHERE id_msg IN ({array_int:messages})', array('messages' => $messages)); // Remove all mentions now that the topic is gone $db->query('', ' DELETE FROM {db_prefix}log_mentions WHERE id_msg IN ({array_int:messages})', array('messages' => $messages)); } // Delete messages in each topic. $db->query('', ' DELETE FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Remove linked calendar events. // @todo if unlinked events are enabled, wouldn't this be expected to keep them? $db->query('', ' DELETE FROM {db_prefix}calendar WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Delete log_topics data $db->query('', ' DELETE FROM {db_prefix}log_topics WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Delete notifications $db->query('', ' DELETE FROM {db_prefix}log_notify WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Delete the topics themselves $db->query('', ' DELETE FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Remove data from the subjects for search cache $db->query('', ' DELETE FROM {db_prefix}log_search_subjects WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); require_once SUBSDIR . '/FollowUps.subs.php'; removeFollowUpsByTopic($topics); foreach ($topics as $topic_id) { cache_put_data('topic_board-' . $topic_id, null, 120); } // Maybe there's an addon that wants to delete topic related data of its own call_integration_hook('integrate_remove_topics', array($topics)); // Update the totals... updateStats('message'); updateTopicStats(); updateSettings(array('calendar_updated' => time())); require_once SUBSDIR . '/Post.subs.php'; $updates = array(); foreach ($adjustBoards as $stats) { $updates[] = $stats['id_board']; } updateLastMessages($updates); }
/** * Browse all the reported posts... * @todo this needs to be given its own file? */ public function action_reportedPosts() { global $txt, $context, $scripturl, $user_info; loadTemplate('ModerationCenter'); require_once SUBSDIR . '/Moderation.subs.php'; // Put the open and closed options into tabs, because we can... $context[$context['moderation_menu_name']]['tab_data'] = array('title' => $txt['mc_reported_posts'], 'help' => '', 'description' => $txt['mc_reported_posts_desc']); // This comes under the umbrella of moderating posts. if ($user_info['mod_cache']['bq'] == '0=1') { isAllowedTo('moderate_forum'); } // Are they wanting to view a particular report? if (!empty($_REQUEST['report'])) { return $this->action_modReport(); } // Set up the comforting bits... $context['page_title'] = $txt['mc_reported_posts']; $context['sub_template'] = 'reported_posts'; // Are we viewing open or closed reports? $context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0; // Are we doing any work? if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid'])) { checkSession('get'); $_GET['rid'] = (int) $_GET['rid']; // Update the report... if (isset($_GET['ignore'])) { updateReportsStatus((int) $_GET['rid'], 'ignore', (int) $_GET['ignore']); } elseif (isset($_GET['close'])) { updateReportsStatus((int) $_GET['rid'], 'close', (int) $_GET['close']); } // Time to update. updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } elseif (isset($_POST['close']) && isset($_POST['close_selected'])) { checkSession('post'); // All the ones to update... $toClose = array(); foreach ($_POST['close'] as $rid) { $toClose[] = (int) $rid; } if (!empty($toClose)) { updateReportsStatus($toClose, 'close', 1); // Time to update. updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } } // How many entries are we viewing? $context['total_reports'] = totalReports($context['view_closed']); // So, that means we can page index, yes? $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reports' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10); $context['start'] = $_GET['start']; // By George, that means we in a position to get the reports, golly good. $context['reports'] = getModReports($context['view_closed'], $context['start'], 10); $report_ids = array_keys($context['reports']); $report_boards_ids = array(); foreach ($context['reports'] as $row) { $context['reports'][$row['id_report']] = array('board' => $row['id_board'], 'id' => $row['id_report'], 'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], 'author' => array('id' => $row['id_author'], 'name' => $row['author_name'], 'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'], 'href' => $scripturl . '?action=profile;u=' . $row['id_author']), 'comments' => array(), 'time_started' => standardTime($row['time_started']), 'last_updated' => standardTime($row['time_updated']), 'subject' => $row['subject'], 'body' => parse_bbc($row['body']), 'num_reports' => $row['num_reports'], 'closed' => $row['closed'], 'ignore' => $row['ignore_all'], 'buttons' => array('quickmod_check' => array('checkbox' => !$context['view_closed'], 'name' => 'close', 'value' => $row['id_report']), 'details' => array('href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], 'text' => $txt['mc_reportedp_details']), 'ignore' => array('href' => $scripturl . '?action=moderate;area=reports' . ($context['view_closed'] ? ';sa=closed' : '') . ';ignore=' . (int) (!$row['ignore_all']) . ';rid=' . $row['id_report'] . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'text' => $row['ignore_all'] ? $txt['mc_reportedp_unignore'] : $txt['mc_reportedp_ignore'], 'custom' => $row['ignore_all'] ? '' : 'onclick="return confirm(' . JavaScriptEscape($txt['mc_reportedp_ignore_confirm']) . ');"'), 'close' => array('href' => $scripturl . '?action=moderate;area=reports' . ($context['view_closed'] ? ';sa=closed' : '') . ';close=' . (int) (!$row['closed']) . ';rid=' . $row['id_report'] . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'text' => $context['view_closed'] ? $txt['mc_reportedp_open'] : $txt['mc_reportedp_close']))); $report_boards_ids[] = $row['id_board']; } // Get the names of boards these topics are in. if (!empty($report_ids)) { require_once SUBSDIR . '/Boards.subs.php'; $board_names = getBoardList(array('included_boards' => $report_boards_ids), true); // Add the board name to the report array foreach ($context['reports'] as $id_report => $report) { if (!empty($board_names[$report['board']])) { $context['reports'][$id_report]['board_name'] = $board_names[$report['board']]['board_name']; } } } // Now get all the people who reported it. if (!empty($report_ids)) { $comments = getReportsUserComments($report_ids); foreach ($comments as $id_rep => $rows) { foreach ($rows as $row) { $context['reports'][$id_rep]['comments'][] = array('id' => $row['id_comment'], 'message' => $row['comment'], 'raw_time' => $row['time_sent'], 'time' => standardTime($row['time_sent']), 'html_time' => htmlTime($row['time_sent']), 'timestamp' => forum_time(true, $row['time_sent']), 'member' => array('id' => $row['id_member'], 'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'], 'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']), 'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '')); } } } }
/** * Loads the number of items awaiting moderation attention * - Only loads the value a given permission level can see * - If supplied a board number will load the values only for that board * - Unapproved posts * - Unapproved topics * - Unapproved attachments * - Failed emails * - Reported posts * - Members awaiting approval (activation, deletion, group requests) * * @param int|null $brd */ function loadModeratorMenuCounts($brd = null) { global $modSettings, $user_info; static $menu_errors = array(); // Work out what boards they can work in! $approve_boards = !empty($user_info['mod_cache']['ap']) ? $user_info['mod_cache']['ap'] : boardsAllowedTo('approve_posts'); // Supplied a specific board to check? if (!empty($brd)) { $filter_board = array((int) $brd); $approve_boards = $approve_boards == array(0) ? $filter_board : array_intersect($approve_boards, $filter_board); } // Work out the query if ($approve_boards == array(0)) { $approve_query = ''; } elseif (!empty($approve_boards)) { $approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')'; } else { $approve_query = ' AND 1=0'; } // Set up the cache key for this permissions level $cache_key = md5($user_info['query_see_board'] . $approve_query . $user_info['mod_cache']['bq'] . $user_info['mod_cache']['gq'] . $user_info['mod_cache']['mq'] . (int) allowedTo('approve_emails') . '_' . (int) allowedTo('moderate_forum')); if (isset($menu_errors[$cache_key])) { return $menu_errors[$cache_key]; } // If its been cached, guess what, thats right use it! $temp = cache_get_data('num_menu_errors', 900); if ($temp === null || !isset($temp[$cache_key])) { // Starting out with nothing is a good start $menu_errors[$cache_key] = array('memberreq' => 0, 'groupreq' => 0, 'attachments' => 0, 'reports' => 0, 'emailmod' => 0, 'postmod' => 0, 'topics' => 0, 'posts' => 0); if ($modSettings['postmod_active'] && !empty($approve_boards)) { $totals = recountUnapprovedPosts($approve_query); $menu_errors[$cache_key]['posts'] = $totals['posts']; $menu_errors[$cache_key]['topics'] = $totals['topics']; // Totals for the menu item unapproved posts and topics $menu_errors[$cache_key]['postmod'] = $menu_errors[$cache_key]['topics'] + $menu_errors[$cache_key]['posts']; } // Attachments if ($modSettings['postmod_active'] && !empty($approve_boards)) { require_once SUBSDIR . '/ManageAttachments.subs.php'; $menu_errors[$cache_key]['attachments'] = list_getNumUnapprovedAttachments($approve_query); } // Reported posts if (!empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1') { $menu_errors[$cache_key]['reports'] = recountOpenReports(false); } // Email failures that require attention if (!empty($modSettings['maillist_enabled']) && allowedTo('approve_emails')) { $menu_errors[$cache_key]['emailmod'] = recountFailedEmails($approve_query); } // Group requests if (!empty($user_info['mod_cache']) && $user_info['mod_cache']['gq'] != '0=1') { $menu_errors[$cache_key]['groupreq'] = count(groupRequests()); } // Member requests if (allowedTo('moderate_forum') && (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2 || !empty($modSettings['approveAccountDeletion']))) { require_once SUBSDIR . '/Members.subs.php'; $awaiting_activation = 0; $activation_numbers = countInactiveMembers(); // 5 = COPPA, 4 = Awaiting Deletion, 3 = Awaiting Approval foreach ($activation_numbers as $activation_type => $total_members) { if (in_array($activation_type, array(3, 4, 5))) { $awaiting_activation += $total_members; } } $menu_errors[$cache_key]['memberreq'] = $awaiting_activation; } // Grand Totals for the top most menus $menu_errors[$cache_key]['pt_total'] = $menu_errors[$cache_key]['emailmod'] + $menu_errors[$cache_key]['postmod'] + $menu_errors[$cache_key]['reports'] + $menu_errors[$cache_key]['attachments']; $menu_errors[$cache_key]['mg_total'] = $menu_errors[$cache_key]['memberreq'] + $menu_errors[$cache_key]['groupreq']; $menu_errors[$cache_key]['grand_total'] = $menu_errors[$cache_key]['pt_total'] + $menu_errors[$cache_key]['mg_total']; // Add this key in to the array, technically this resets the cache time for all keys // done this way as the entire thing needs to go null once *any* moderation action is taken $menu_errors = is_array($temp) ? array_merge($temp, $menu_errors) : $menu_errors; // Store it away for a while, not like this should change that often cache_put_data('num_menu_errors', $menu_errors, 900); } else { $menu_errors = $temp === null ? array() : $temp; } return $menu_errors[$cache_key]; }
/** * Log changes to the forum, such as moderation events or administrative changes. * * - This behaves just like logAction() did, except that it is designed to * log multiple actions at once. * * @param mixed[] $logs array of actions to log [] = array(action => log_type=> extra=>) * @return int the last logged ID */ function logActions($logs) { global $modSettings, $user_info; $db = database(); $inserts = array(); $log_types = array('moderate' => 1, 'user' => 2, 'admin' => 3); call_integration_hook('integrate_log_types', array(&$log_types)); // No point in doing anything, if the log isn't even enabled. if (empty($modSettings['modlog_enabled'])) { return false; } foreach ($logs as $log) { if (!isset($log_types[$log['log_type']])) { return false; } // Do we have something to log here, after all? if (!is_array($log['extra'])) { trigger_error('logActions(): data is not an array with action \'' . $log['action'] . '\'', E_USER_NOTICE); } // Pull out the parts we want to store separately, but also make sure that the data is proper if (isset($log['extra']['topic'])) { if (!is_numeric($log['extra']['topic'])) { trigger_error('logActions(): data\'s topic is not a number', E_USER_NOTICE); } $topic_id = empty($log['extra']['topic']) ? 0 : (int) $log['extra']['topic']; unset($log['extra']['topic']); } else { $topic_id = 0; } if (isset($log['extra']['message'])) { if (!is_numeric($log['extra']['message'])) { trigger_error('logActions(): data\'s message is not a number', E_USER_NOTICE); } $msg_id = empty($log['extra']['message']) ? 0 : (int) $log['extra']['message']; unset($log['extra']['message']); } else { $msg_id = 0; } // @todo cache this? // Is there an associated report on this? if (in_array($log['action'], array('move', 'remove', 'split', 'merge'))) { $request = $db->query('', ' SELECT id_report FROM {db_prefix}log_reported WHERE {raw:column_name} = {int:reported} LIMIT 1', array('column_name' => !empty($msg_id) ? 'id_msg' : 'id_topic', 'reported' => !empty($msg_id) ? $msg_id : $topic_id)); // Alright, if we get any result back, update open reports. if ($db->num_rows($request) > 0) { require_once SUBSDIR . '/Moderation.subs.php'; updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); } $db->free_result($request); } if (isset($log['extra']['member']) && !is_numeric($log['extra']['member'])) { trigger_error('logActions(): data\'s member is not a number', E_USER_NOTICE); } if (isset($log['extra']['board'])) { if (!is_numeric($log['extra']['board'])) { trigger_error('logActions(): data\'s board is not a number', E_USER_NOTICE); } $board_id = empty($log['extra']['board']) ? 0 : (int) $log['extra']['board']; unset($log['extra']['board']); } else { $board_id = 0; } if (isset($log['extra']['board_to'])) { if (!is_numeric($log['extra']['board_to'])) { trigger_error('logActions(): data\'s board_to is not a number', E_USER_NOTICE); } if (empty($board_id)) { $board_id = empty($log['extra']['board_to']) ? 0 : (int) $log['extra']['board_to']; unset($log['extra']['board_to']); } } if (isset($log['extra']['member_affected'])) { $memID = $log['extra']['member_affected']; } else { $memID = $user_info['id']; } $inserts[] = array(time(), $log_types[$log['log_type']], $memID, $user_info['ip'], $log['action'], $board_id, $topic_id, $msg_id, serialize($log['extra'])); } $db->insert('', '{db_prefix}log_actions', array('log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534'), $inserts, array('id_action')); return $db->insert_id('{db_prefix}log_actions', 'id_action'); }