0x1949 Team - FAZEMRX - MANAGER
Edit File: Modlog.php
<?php /** * The moderation log is this file's only job. * It views it, and that's about all it does. * * Simple Machines Forum (SMF) * * @package SMF * @author Simple Machines https://www.simplemachines.org * @copyright 2022 Simple Machines and individual contributors * @license https://www.simplemachines.org/about/smf/license.php BSD * * @version 2.1.0 */ if (!defined('SMF')) die('No direct access...'); /** * Prepares the information from the moderation log for viewing. * Show the moderation log. * If clearing the log, leaves a message in the log to indicate it was cleared, by whom and when. * Requires the admin_forum permission. * Accessed via ?action=moderate;area=modlog. * * Uses Modlog template, main sub-template. */ function ViewModlog() { global $txt, $context, $scripturl, $sourcedir, $smcFunc; // Are we looking at the moderation log or the administration log. $context['log_type'] = isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'adminlog' ? 3 : 1; if ($context['log_type'] == 3) isAllowedTo('admin_forum'); // These change dependant on whether we are viewing the moderation or admin log. if ($context['log_type'] == 3 || $_REQUEST['action'] == 'admin') $context['url_start'] = '?action=admin;area=logs;sa=' . ($context['log_type'] == 3 ? 'adminlog' : 'modlog') . ';type=' . $context['log_type']; else $context['url_start'] = '?action=moderate;area=modlog;type=' . $context['log_type']; $context['can_delete'] = allowedTo('admin_forum'); loadLanguage('Admin+Modlog'); $context['page_title'] = $context['log_type'] == 3 ? $txt['modlog_admin_log'] : $txt['modlog_view']; // The number of entries to show per page of log file. $context['displaypage'] = 30; // Actions whose log entries cannot be deleted. $context['uneditable_actions'] = array('agreement_updated', 'policy_updated'); // Handle deletion... if (isset($_POST['removeall']) && $context['can_delete']) { checkSession(); validateToken('mod-ml'); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_actions WHERE id_log = {int:moderate_log} AND action NOT IN ({array_string:uneditable})', array( 'moderate_log' => $context['log_type'], 'uneditable' => $context['uneditable_actions'], ) ); $log_type = isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'adminlog' ? 'admin' : 'moderate'; logAction('clearlog_' . $log_type, array(), $log_type); } elseif (!empty($_POST['remove']) && isset($_POST['delete']) && $context['can_delete']) { checkSession(); validateToken('mod-ml'); // No sneaky removing the 'cleared the log' entries. $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_actions WHERE id_log = {int:moderate_log} AND id_action IN ({array_string:delete_actions}) AND action NOT LIKE {string:clearlog} AND action NOT IN ({array_string:uneditable})', array( 'delete_actions' => array_unique($_POST['delete']), 'moderate_log' => $context['log_type'], 'uneditable' => $context['uneditable_actions'], 'clearlog' => 'clearlog_%', ) ); } // Do the column stuff! $sort_types = array( 'action' => 'lm.action', 'time' => 'lm.log_time', 'member' => 'mem.real_name', 'group' => 'mg.group_name', 'ip' => 'lm.ip', ); // Setup the direction stuff... $context['order'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'time'; // If we're coming from a search, get the variables. if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search'])) { $search_params = base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))); $search_params = $smcFunc['json_decode']($search_params, true); } // This array houses all the valid search types. $searchTypes = array( 'action' => array('sql' => 'lm.action', 'label' => $txt['modlog_action']), 'member' => array('sql' => 'mem.real_name', 'label' => $txt['modlog_member']), 'group' => array('sql' => 'mg.group_name', 'label' => $txt['modlog_position']), 'ip' => array('sql' => 'lm.ip', 'label' => $txt['modlog_ip']) ); if (!isset($search_params['string']) || (!empty($_REQUEST['search']) && $search_params['string'] != $_REQUEST['search'])) $search_params_string = empty($_REQUEST['search']) ? '' : $_REQUEST['search']; else $search_params_string = $search_params['string']; if (isset($_REQUEST['search_type']) || empty($search_params['type']) || !isset($searchTypes[$search_params['type']])) $search_params_type = isset($_REQUEST['search_type']) && isset($searchTypes[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($searchTypes[$context['order']]) ? $context['order'] : 'member'); else $search_params_type = $search_params['type']; $search_params_column = $searchTypes[$search_params_type]['sql']; $search_params = array( 'string' => $search_params_string, 'type' => $search_params_type, ); // Setup the search context. $context['search_params'] = empty($search_params['string']) ? '' : base64_encode($smcFunc['json_encode']($search_params)); $context['search'] = array( 'string' => $search_params['string'], 'type' => $search_params['type'], 'label' => $searchTypes[$search_params_type]['label'], ); // If they are searching by action, then we must do some manual intervention to search in their language! if ($search_params['type'] == 'action' && !empty($search_params['string'])) { // For the moment they can only search for ONE action! foreach ($txt as $key => $text) { if (substr($key, 0, 10) == 'modlog_ac_' && strpos($text, $search_params['string']) !== false) { $search_params['string'] = substr($key, 10); break; } } } require_once($sourcedir . '/Subs-List.php'); // This is all the information required for a watched user listing. $listOptions = array( 'id' => 'moderation_log_list', 'title' => $context['log_type'] == 3 ? $txt['admin_log'] : $txt['moderation_log'], 'width' => '100%', 'items_per_page' => $context['displaypage'], 'no_items_label' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin_log_' : '') . 'no_entries_found'], 'base_href' => $scripturl . $context['url_start'] . (!empty($context['search_params']) ? ';params=' . $context['search_params'] : ''), 'default_sort_col' => 'time', 'get_items' => array( 'function' => 'list_getModLogEntries', 'params' => array( (!empty($search_params['string']) ? ' INSTR({raw:sql_type}, {string:search_string}) > 0' : ''), array('sql_type' => $search_params_column, 'search_string' => $search_params['string']), $context['log_type'], ), ), 'get_count' => array( 'function' => 'list_getModLogEntryCount', 'params' => array( (!empty($search_params['string']) ? ' INSTR({raw:sql_type}, {string:search_string}) > 0' : ''), array('sql_type' => $search_params_column, 'search_string' => $search_params['string']), $context['log_type'], ), ), // This assumes we are viewing by user. 'columns' => array( 'action' => array( 'header' => array( 'value' => $txt['modlog_action'], 'class' => 'lefttext', ), 'data' => array( 'db' => 'action_text', 'class' => 'smalltext', ), 'sort' => array( 'default' => 'lm.action', 'reverse' => 'lm.action DESC', ), ), 'time' => array( 'header' => array( 'value' => $txt['modlog_date'], 'class' => 'lefttext', ), 'data' => array( 'db' => 'time', 'class' => 'smalltext', ), 'sort' => array( 'default' => 'lm.log_time DESC', 'reverse' => 'lm.log_time', ), ), 'moderator' => array( 'header' => array( 'value' => $txt['modlog_member'], 'class' => 'lefttext', ), 'data' => array( 'db' => 'moderator_link', 'class' => 'smalltext', ), 'sort' => array( 'default' => 'mem.real_name', 'reverse' => 'mem.real_name DESC', ), ), 'position' => array( 'header' => array( 'value' => $txt['modlog_position'], 'class' => 'lefttext', ), 'data' => array( 'db' => 'position', 'class' => 'smalltext', ), 'sort' => array( 'default' => 'mg.group_name', 'reverse' => 'mg.group_name DESC', ), ), 'ip' => array( 'header' => array( 'value' => $txt['modlog_ip'], 'class' => 'lefttext', ), 'data' => array( 'db' => 'ip', 'class' => 'smalltext', ), 'sort' => array( 'default' => 'lm.ip', 'reverse' => 'lm.ip DESC', ), ), 'delete' => array( 'header' => array( 'value' => '<input type="checkbox" name="all" onclick="invertAll(this, this.form);">', 'class' => 'centercol', ), 'data' => array( 'function' => function($entry) { return '<input type="checkbox" name="delete[]" value="' . $entry['id'] . '"' . ($entry['editable'] ? '' : ' disabled') . '>'; }, 'class' => 'centercol', ), ), ), 'form' => array( 'href' => $scripturl . $context['url_start'], 'include_sort' => true, 'include_start' => true, 'hidden_fields' => array( $context['session_var'] => $context['session_id'], 'params' => $context['search_params'] ), 'token' => 'mod-ml', ), 'additional_rows' => array( array( 'position' => 'after_title', 'value' => ' ' . $txt['modlog_search'] . ' (' . $txt['modlog_by'] . ': ' . $context['search']['label'] . '): <input type="text" name="search" size="18" value="' . $smcFunc['htmlspecialchars']($context['search']['string']) . '"> <input type="submit" name="is_search" value="' . $txt['modlog_go'] . '" class="button" style="float:none"> ' . ($context['can_delete'] ? ' <input type="submit" name="remove" value="' . $txt['modlog_remove'] . '" data-confirm="' . $txt['modlog_remove_selected_confirm'] . '" class="button you_sure"> <input type="submit" name="removeall" value="' . $txt['modlog_removeall'] . '" data-confirm="' . $txt['modlog_remove_all_confirm'] . '" class="button you_sure">' : ''), 'class' => '', ), array( 'position' => 'below_table_data', 'value' => $context['can_delete'] ? ' <input type="submit" name="remove" value="' . $txt['modlog_remove'] . '" data-confirm="' . $txt['modlog_remove_selected_confirm'] . '" class="button you_sure"> <input type="submit" name="removeall" value="' . $txt['modlog_removeall'] . '" data-confirm="' . $txt['modlog_remove_all_confirm'] . '" class="button you_sure">' : '', 'class' => 'floatright', ), ), ); // Overriding this with a hook? $moderation_menu_name = array(); call_integration_hook('integrate_viewModLog', array(&$listOptions, &$moderation_menu_name)); createToken('mod-ml'); // Create the watched user list. createList($listOptions); $context['sub_template'] = 'show_list'; $context['default_list'] = 'moderation_log_list'; // If a hook has changed this, respect it. if (!empty($moderation_menu_name)) $context[$context['moderation_menu_name']]['tab_data'] = $moderation_menu_name; elseif (isset($context['moderation_menu_name'])) $context[$context['moderation_menu_name']]['tab_data'] = array( 'title' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log'], 'help' => $context['log_type'] == 3 ? 'adminlog' : 'modlog', 'description' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log_desc'] ); } /** * Get the number of mod log entries. * Callback for createList() in ViewModlog(). * * @param string $query_string An extra string for the WHERE clause in the query to further filter results * @param array $query_params An array of parameters for the query_string * @param int $log_type The log type (1 for mod log, 3 for admin log) * @param bool $ignore_boards Whether to ignore board restrictions */ function list_getModLogEntryCount($query_string = '', $query_params = array(), $log_type = 1, $ignore_boards = false) { global $smcFunc, $user_info; $modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : (($user_info['mod_cache']['bq'] == '0=1' || $ignore_boards) ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board')))); $result = $smcFunc['db_query']('', ' SELECT COUNT(*) FROM {db_prefix}log_actions AS lm LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lm.id_member) LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END) LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lm.id_board) LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lm.id_topic) WHERE id_log = {int:log_type} AND {raw:modlog_query}' . (!empty($query_string) ? ' AND ' . $query_string : ''), array_merge($query_params, array( 'reg_group_id' => 0, 'log_type' => $log_type, 'modlog_query' => $modlog_query, )) ); list ($entry_count) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); return $entry_count; } /** * Gets the moderation log entries that match the specified parameters. * Callback for createList() in ViewModlog(). * * @param int $start The item to start with (for pagination purposes) * @param int $items_per_page The number of items to show per page * @param string $sort A string indicating how to sort the results * @param string $query_string An extra string for the WHERE clause of the query, to further filter results * @param array $query_params An array of parameters for the query string * @param int $log_type The log type - 1 for mod log or 3 for admin log * @param bool $ignore_boards Whether to ignore board restrictions * @return array An array of info about the mod log entries */ function list_getModLogEntries($start, $items_per_page, $sort, $query_string = '', $query_params = array(), $log_type = 1, $ignore_boards = false) { global $scripturl, $txt, $smcFunc, $user_info, $context; $modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : (($user_info['mod_cache']['bq'] == '0=1' || $ignore_boards) ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board')))); if (!isset($context['uneditable_actions'])) $context['uneditable_actions'] = array(); // Can they see the IP address? $seeIP = allowedTo('moderate_forum'); // Here we have the query getting the log details. $result = $smcFunc['db_query']('', ' SELECT lm.id_action, lm.id_member, lm.ip, lm.log_time, lm.action, lm.id_board, lm.id_topic, lm.id_msg, lm.extra, mem.real_name, mg.group_name FROM {db_prefix}log_actions AS lm LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lm.id_member) LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END) LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lm.id_board) LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lm.id_topic) WHERE id_log = {int:log_type} AND {raw:modlog_query}' . (!empty($query_string) ? ' AND ' . $query_string : '') . ' ORDER BY {raw:sort} LIMIT {int:start}, {int:max}', array_merge($query_params, array( 'reg_group_id' => 0, 'log_type' => $log_type, 'modlog_query' => $modlog_query, 'sort' => $sort, 'start' => $start, 'max' => $items_per_page, )) ); // Arrays for decoding objects into. $topics = array(); $boards = array(); $members = array(); $messages = array(); $entries = array(); while ($row = $smcFunc['db_fetch_assoc']($result)) { $row['extra'] = $smcFunc['json_decode']($row['extra'], true); // Corrupt? $row['extra'] = is_array($row['extra']) ? $row['extra'] : array(); // Add on some of the column stuff info if (!empty($row['id_board'])) { if ($row['action'] == 'move') $row['extra']['board_to'] = $row['id_board']; else $row['extra']['board'] = $row['id_board']; } if (!empty($row['id_topic'])) $row['extra']['topic'] = $row['id_topic']; if (!empty($row['id_msg'])) $row['extra']['message'] = $row['id_msg']; // Is this associated with a topic? if (isset($row['extra']['topic'])) $topics[(int) $row['extra']['topic']][] = $row['id_action']; if (isset($row['extra']['new_topic'])) $topics[(int) $row['extra']['new_topic']][] = $row['id_action']; // How about a member? if (isset($row['extra']['member'])) { // Guests don't have names! if (empty($row['extra']['member'])) $row['extra']['member'] = $txt['modlog_parameter_guest']; else { // Try to find it... $members[(int) $row['extra']['member']][] = $row['id_action']; } } // Associated with a board? if (isset($row['extra']['board_to'])) $boards[(int) $row['extra']['board_to']][] = $row['id_action']; if (isset($row['extra']['board_from'])) $boards[(int) $row['extra']['board_from']][] = $row['id_action']; if (isset($row['extra']['board'])) $boards[(int) $row['extra']['board']][] = $row['id_action']; // A message? if (isset($row['extra']['message'])) $messages[(int) $row['extra']['message']][] = $row['id_action']; // IP Info? if (isset($row['extra']['ip_range'])) if ($seeIP) $row['extra']['ip_range'] = '<a href="' . $scripturl . '?action=trackip;searchip=' . $row['extra']['ip_range'] . '">' . $row['extra']['ip_range'] . '</a>'; else $row['extra']['ip_range'] = $txt['logged']; // Email? if (isset($row['extra']['email'])) $row['extra']['email'] = '<a href="mailto:' . $row['extra']['email'] . '">' . $row['extra']['email'] . '</a>'; // Bans are complex. if ($row['action'] == 'ban' || $row['action'] == 'banremove') { $row['action_text'] = $txt['modlog_ac_ban' . ($row['action'] == 'banremove' ? '_remove' : '')]; foreach (array('member', 'email', 'ip_range', 'hostname') as $type) if (isset($row['extra'][$type])) $row['action_text'] .= $txt['modlog_ac_ban_trigger_' . $type]; } // The array to go to the template. Note here that action is set to a "default" value of the action doesn't match anything in the descriptions. Allows easy adding of logging events with basic details. $entries[$row['id_action']] = array( 'id' => $row['id_action'], 'ip' => $seeIP ? inet_dtop($row['ip']) : $txt['logged'], 'position' => empty($row['real_name']) && empty($row['group_name']) ? $txt['guest'] : $row['group_name'], 'moderator_link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>' : (empty($row['real_name']) ? ($txt['guest'] . (!empty($row['extra']['member_acted']) ? ' (' . $row['extra']['member_acted'] . ')' : '')) : $row['real_name']), 'time' => timeformat($row['log_time']), 'timestamp' => $row['log_time'], 'editable' => substr($row['action'], 0, 8) !== 'clearlog' && !in_array($row['action'], $context['uneditable_actions']), 'extra' => $row['extra'], 'action' => $row['action'], 'action_text' => isset($row['action_text']) ? $row['action_text'] : '', ); } $smcFunc['db_free_result']($result); if (!empty($boards)) { $request = $smcFunc['db_query']('', ' SELECT id_board, name FROM {db_prefix}boards WHERE id_board IN ({array_int:board_list}) LIMIT {int:limit}', array( 'board_list' => array_keys($boards), 'limit' => count(array_keys($boards)), ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { foreach ($boards[$row['id_board']] as $action) { // Make the board number into a link - dealing with moving too. if (isset($entries[$action]['extra']['board_to']) && $entries[$action]['extra']['board_to'] == $row['id_board']) $entries[$action]['extra']['board_to'] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>'; elseif (isset($entries[$action]['extra']['board_from']) && $entries[$action]['extra']['board_from'] == $row['id_board']) $entries[$action]['extra']['board_from'] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>'; elseif (isset($entries[$action]['extra']['board']) && $entries[$action]['extra']['board'] == $row['id_board']) $entries[$action]['extra']['board'] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>'; } } $smcFunc['db_free_result']($request); } if (!empty($topics)) { $request = $smcFunc['db_query']('', ' SELECT ms.subject, t.id_topic FROM {db_prefix}topics AS t INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg) WHERE t.id_topic IN ({array_int:topic_list}) LIMIT {int:limit}', array( 'topic_list' => array_keys($topics), 'limit' => count(array_keys($topics)), ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { foreach ($topics[$row['id_topic']] as $action) { $this_action = &$entries[$action]; // This isn't used in the current theme. $this_action['topic'] = array( 'id' => $row['id_topic'], 'subject' => $row['subject'], 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>' ); // Make the topic number into a link - dealing with splitting too. if (isset($this_action['extra']['topic']) && $this_action['extra']['topic'] == $row['id_topic']) $this_action['extra']['topic'] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.' . (isset($this_action['extra']['message']) ? 'msg' . $this_action['extra']['message'] . '#msg' . $this_action['extra']['message'] : '0') . '">' . $row['subject'] . '</a>'; elseif (isset($this_action['extra']['new_topic']) && $this_action['extra']['new_topic'] == $row['id_topic']) $this_action['extra']['new_topic'] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.' . (isset($this_action['extra']['message']) ? 'msg' . $this_action['extra']['message'] . '#msg' . $this_action['extra']['message'] : '0') . '">' . $row['subject'] . '</a>'; } } $smcFunc['db_free_result']($request); } if (!empty($messages)) { $request = $smcFunc['db_query']('', ' SELECT id_msg, subject FROM {db_prefix}messages WHERE id_msg IN ({array_int:message_list}) LIMIT {int:limit}', array( 'message_list' => array_keys($messages), 'limit' => count(array_keys($messages)), ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { foreach ($messages[$row['id_msg']] as $action) { $this_action = &$entries[$action]; // This isn't used in the current theme. $this_action['message'] = array( 'id' => $row['id_msg'], 'subject' => $row['subject'], 'href' => $scripturl . '?msg=' . $row['id_msg'], 'link' => '<a href="' . $scripturl . '?msg=' . $row['id_msg'] . '">' . $row['subject'] . '</a>', ); // Make the message number into a link. if (isset($this_action['extra']['message']) && $this_action['extra']['message'] == $row['id_msg']) $this_action['extra']['message'] = '<a href="' . $scripturl . '?msg=' . $row['id_msg'] . '">' . $row['subject'] . '</a>'; } } $smcFunc['db_free_result']($request); } if (!empty($members)) { $request = $smcFunc['db_query']('', ' SELECT real_name, id_member FROM {db_prefix}members WHERE id_member IN ({array_int:member_list}) LIMIT {int:limit}', array( 'member_list' => array_keys($members), 'limit' => count(array_keys($members)), ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { foreach ($members[$row['id_member']] as $action) { // Not used currently. $entries[$action]['member'] = array( 'id' => $row['id_member'], 'name' => $row['real_name'], 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>' ); // Make the member number into a name. $entries[$action]['extra']['member'] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>'; } } $smcFunc['db_free_result']($request); } // Do some formatting of the action string. foreach ($entries as $k => $entry) { // Make any message info links so its easier to go find that message. if (isset($entry['extra']['message']) && (empty($entry['message']) || empty($entry['message']['id']))) $entries[$k]['extra']['message'] = '<a href="' . $scripturl . '?msg=' . $entry['extra']['message'] . '">' . $entry['extra']['message'] . '</a>'; // Mark up any deleted members, topics and boards. foreach (array('board', 'board_from', 'board_to', 'member', 'topic', 'new_topic') as $type) if (!empty($entry['extra'][$type]) && is_numeric($entry['extra'][$type])) $entries[$k]['extra'][$type] = sprintf($txt['modlog_id'], $entry['extra'][$type]); if (isset($entry['extra']['report'])) { // Member profile reports go in a different area if (stristr($entry['action'], 'user_report')) $entries[$k]['extra']['report'] = '<a href="' . $scripturl . '?action=moderate;area=reportedmembers;sa=details;rid=' . $entry['extra']['report'] . '">' . $txt['modlog_report'] . '</a>'; else $entries[$k]['extra']['report'] = '<a href="' . $scripturl . '?action=moderate;area=reportedposts;sa=details;rid=' . $entry['extra']['report'] . '">' . $txt['modlog_report'] . '</a>'; } if (empty($entries[$k]['action_text'])) $entries[$k]['action_text'] = isset($txt['modlog_ac_' . $entry['action']]) ? $txt['modlog_ac_' . $entry['action']] : $entry['action']; $entries[$k]['action_text'] = preg_replace_callback( '~\{([A-Za-z\d_]+)\}~i', function($matches) use ($entries, $k) { return isset($entries[$k]['extra'][$matches[1]]) ? $entries[$k]['extra'][$matches[1]] : ''; }, $entries[$k]['action_text'] ); } // Back we go! return $entries; } ?>