function updateStatisticsForPost($post, $new = true) { $postIp = isset($post['ip']) ? $post['ip'] : $_SERVER['REMOTE_ADDR']; $postUri = $post['board']; $postTime = (int) ($post['time'] / 3600) * 3600; $bsQuery = prepare("SELECT * FROM ``board_stats`` WHERE `stat_uri` = :uri AND `stat_hour` = :hour"); $bsQuery->bindValue(':uri', $postUri); $bsQuery->bindValue(':hour', $postTime, PDO::PARAM_INT); $bsQuery->execute() or error(db_error($bsQuery)); $bsResult = $bsQuery->fetchAll(PDO::FETCH_ASSOC); // Flesh out the new stats row. $boardStats = array(); // If we already have a row, we're going to be adding this post to it. if (count($bsResult)) { $boardStats = $bsResult[0]; $boardStats['stat_uri'] = $postUri; $boardStats['stat_hour'] = $postTime; $boardStats['post_id_array'] = unserialize($boardStats['post_id_array']); $boardStats['author_ip_array'] = unserialize($boardStats['author_ip_array']); ++$boardStats['post_count']; $boardStats['post_id_array'][] = (int) $post['id']; $boardStats['author_ip_array'][] = less_ip($postIp); $boardStats['author_ip_array'] = array_unique($boardStats['author_ip_array']); } else { $boardStats['stat_uri'] = $postUri; $boardStats['stat_hour'] = $postTime; $boardStats['post_count'] = 1; $boardStats['post_id_array'] = array((int) $post['id']); $boardStats['author_ip_count'] = 1; $boardStats['author_ip_array'] = array(less_ip($postIp)); } // Cleanly serialize our array for insertion. $boardStats['post_id_array'] = str_replace("\"", "\\\"", serialize($boardStats['post_id_array'])); $boardStats['author_ip_array'] = str_replace("\"", "\\\"", serialize($boardStats['author_ip_array'])); // Insert this data into our statistics table. $statsInsert = "VALUES(\"{$boardStats['stat_uri']}\", \"{$boardStats['stat_hour']}\", \"{$boardStats['post_count']}\", \"{$boardStats['post_id_array']}\", \"{$boardStats['author_ip_count']}\", \"{$boardStats['author_ip_array']}\" )"; $postStatQuery = prepare("REPLACE INTO ``board_stats`` (stat_uri, stat_hour, post_count, post_id_array, author_ip_count, author_ip_array) {$statsInsert}"); $postStatQuery->execute() or error(db_error($postStatQuery)); // Update the posts_total tracker on the board. if ($new) { query("UPDATE ``boards`` SET `posts_total`=`posts_total`+1 WHERE `uri`=\"{$postUri}\""); } return $boardStats; }
function twig_less_ip($ip, $board = '') { return less_ip($ip, $board); }
public static function stream_json($out = false, $filter_ips = false, $filter_staff = false, $board_access = false) { global $config, $pdo; if ($board_access && $board_access[0] == '*') { $board_access = false; } $query_addition = ""; if ($board_access) { $boards = implode(", ", array_map(array($pdo, "quote"), $board_access)); $query_addition .= "WHERE `board` IN (" . $boards . ")"; } if ($board_access !== FALSE) { if (!$query_addition) { $query_addition .= " WHERE (`public_bans` IS TRUE) OR ``bans``.`board` IS NULL"; } } $query = query("SELECT ``bans``.*, `username`, `type` FROM ``bans``\n\t\t\tLEFT JOIN ``mods`` ON ``mods``.`id` = `creator`\n\t\t\tLEFT JOIN ``boards`` ON ``boards``.`uri` = ``bans``.`board`\n\t\t\t\t{$query_addition}\n \t\t\tORDER BY `created` DESC") or error(db_error()); $bans = $query->fetchAll(PDO::FETCH_ASSOC); $out ? fputs($out, "[") : (print "["); $end = end($bans); foreach ($bans as &$ban) { $ban['mask'] = self::range_to_string(array($ban['ipstart'], $ban['ipend'])); if ($ban['post']) { $post = json_decode($ban['post']); if ($post) { $ban['message'] = $post->body; } } unset($ban['ipstart'], $ban['ipend'], $ban['post'], $ban['creator']); if ($board_access === false || in_array($ban['board'], $board_access)) { $ban['access'] = true; } if (filter_var($ban['mask'], FILTER_VALIDATE_IP) !== false) { $ban['single_addr'] = true; } if ($filter_staff || $board_access !== false && !in_array($ban['board'], $board_access)) { switch ($ban['type']) { case ADMIN: $ban['username'] = '******'; break; case GLOBALVOLUNTEER: $ban['username'] = '******'; break; case MOD: $ban['username'] = '******'; break; case BOARDVOLUNTEER: $ban['username'] = '******'; break; default: $ban['username'] = '******'; } $ban['vstaff'] = true; } unset($ban['type']); if ($filter_ips || $board_access !== false && !in_array($ban['board'], $board_access)) { $ban['mask'] = @less_ip($ban['mask'], $ban['board']); $ban['masked'] = true; } $json = json_encode($ban); $out ? fputs($out, $json) : (print $json); if ($ban['id'] != $end['id']) { $out ? fputs($out, ",") : (print ","); } } $out ? fputs($out, "]") : (print "]"); }
function mod_reports() { global $config, $mod; // Parse arguments. $urlArgs = func_get_args(); $global = in_array("global", $urlArgs); $json = in_array("json", $urlArgs); if (!hasPermission($config['mod']['reports'])) { error($config['error']['noaccess']); } if ($mod['type'] < GLOBALVOLUNTEER and $global) { error($config['error']['noaccess']); } // Limit reports to ONLY those in our scope. $report_scope = $global ? "global" : "local"; // Get REPORTS. $query = prepare("SELECT * FROM ``reports`` WHERE " . ($mod["type"] < GLOBALVOLUNTEER ? "board = :board AND" : "") . " ``" . ($global ? "global" : "local") . "`` = TRUE LIMIT :limit"); // Limit reports by board if the moderator is local. if ($mod['type'] < GLOBALVOLUNTEER) { $query->bindValue(':board', $mod['boards'][0]); } // Limit by config ceiling. $query->bindValue(':limit', $config['mod']['recent_reports'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); $reports = $query->fetchAll(PDO::FETCH_ASSOC); // Cut off here if we don't have any reports. $reportCount = 0; $reportHTML = ''; if (count($reports) > 0) { // Build queries to fetch content. $report_queries = array(); foreach ($reports as $report) { if (!isset($report_queries[$report['board']])) { $report_queries[$report['board']] = array(); } $report_queries[$report['board']][] = $report['post']; } // Get reported CONTENT. $report_posts = array(); foreach ($report_queries as $board => $posts) { $report_posts[$board] = array(); $query = query(sprintf('SELECT * FROM ``posts_%s`` WHERE `id` = ' . implode(' OR `id` = ', $posts), $board)) or error(db_error()); while ($post = $query->fetch(PDO::FETCH_ASSOC)) { $report_posts[$board][$post['id']] = $post; } } // Develop an associative array of posts to reports. $report_index = array(); foreach ($reports as &$report) { // Delete reports which are for removed content. if (!isset($report_posts[$report['board']][$report['post']])) { // Invalid report (post has since been deleted) $query = prepare("DELETE FROM ``reports`` WHERE `post` = :id AND `board` = :board"); $query->bindValue(':id', $report['post'], PDO::PARAM_INT); $query->bindValue(':board', $report['board']); $query->execute() or error(db_error($query)); continue; } // Build a unique ID. $content_key = "{$report['board']}.{$report['post']}"; // Create a dummy array if it doesn't already exist. if (!isset($report_index[$content_key])) { $report_index[$content_key] = array("board_id" => $report['board'], "post_id" => $report['post'], "content" => $report_posts[$report['board']][$report['post']], "reports" => array()); } // Add the report to the list of reports. $report_index[$content_key]['reports'][$report['id']] = $report; // Increment the total report count. ++$reportCount; } // Only continue if we have something to do. // If there are no valid reports left, we're done. if ($reportCount > 0 && !$json) { // Sort this report index by number of reports, desc. usort($report_index, function ($a, $b) { $ra = count($a['reports']); $rb = count($b['reports']); if ($ra < $rb) { return 1; } else { if ($rb > $ra) { return -1; } else { return 0; } } }); // Loop through the custom index. foreach ($report_index as &$report_item) { $content = $report_item['content']; // Load board content. openBoard($report_item['board_id']); // Load the reported content. if (!$content['thread']) { // Still need to fix this: $po = new Thread($content, '?/', $mod, false); } else { $po = new Post($content, '?/', $mod); } // Fetch clean status. $po->getClean(true); $clean = $po->clean; // Add each report's template to this container. $report_html = ""; $reports_can_demote = false; $reports_can_promote = false; $content_reports = 0; foreach ($report_item['reports'] as $report) { $uri_report_base = "reports/" . ($global ? "global/" : "") . $report['id']; $report_html .= Element('mod/report.html', array('report' => $report, 'config' => $config, 'mod' => $mod, 'global' => $global, 'clean' => $clean, 'uri_dismiss' => "?/{$uri_report_base}/dismiss", 'uri_ip' => "?/{$uri_report_base}/dismissall", 'uri_demote' => "?/{$uri_report_base}/demote", 'uri_promote' => "?/{$uri_report_base}/promote", 'token_dismiss' => make_secure_link_token($uri_report_base . '/dismiss'), 'token_ip' => make_secure_link_token($uri_report_base . '/dismissall'), 'token_demote' => make_secure_link_token($uri_report_base . '/demote'), 'token_promote' => make_secure_link_token($uri_report_base . '/promote'))); // Determines if we can "Demote All" / "Promote All" // This logic only needs one instance of a demotable or promotable report to work. // DEMOTE can occur when we're global and the report has a 1 for local (meaning locally, it's not dismissed) // PROMOTE can occur when we're local and the report has a 0 for global (meaning it's not global). if ($global && $report['local'] == "1") { $reports_can_demote = true; } else { if (!$global && $report['global'] != "1") { $reports_can_promote = true; } } ++$content_reports; } // Build the ">>>/b/ thread reported 3 times" title. $report_title = sprintf(_('<a href="%s" title="View content" target="_new">>>>/%s/</a> %s reported %d time(s).'), "?/{$report_item['board_id']}/res/" . ($content['thread'] ?: $content['id']) . ".html#{$content['thread']}", $report_item['board_id'], _($content['thread'] ? "reply" : "thread"), $content_reports); // Figure out some stuff we need for the page. $reports_can_demote = $clean['clean_local'] ? false : $reports_can_demote; $reports_can_promote = $clean['clean_global'] ? false : $reports_can_promote; $uri_content_base = "reports/" . ($global ? "global/" : "") . "content/"; $uri_clean_base = "reports/" . ($global ? "global/" : "") . "{$report_item['board_id']}/clean/{$content['id']}"; // Build the actions page. $content_html = Element('mod/report_content.html', array('reports_html' => $report_html, 'reports_can_demote' => $reports_can_demote, 'reports_can_promote' => $reports_can_promote, 'report_count' => $content_reports, 'report_title' => $report_title, 'content_html' => $po->build(true), 'content_board' => $report_item['board_id'], 'content' => (array) $content, 'clean' => $clean, 'uri_content_demote' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/demote", 'uri_content_promote' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/promote", 'uri_content_dismiss' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/dismiss", 'token_content_demote' => make_secure_link_token("{$uri_content_base}{$report_item['board_id']}/{$content['id']}/demote"), 'token_content_promote' => make_secure_link_token("{$uri_content_base}{$report_item['board_id']}/{$content['id']}/promote"), 'token_content_dismiss' => make_secure_link_token("{$uri_content_base}{$report_item['board_id']}/{$content['id']}/dismiss"), 'uri_clean' => "?/{$uri_clean_base}/local", 'uri_clean_global' => "?/{$uri_clean_base}/global", 'uri_clean_both' => "?/{$uri_clean_base}/global+local", 'token_clean' => make_secure_link_token($uri_clean_base . '/local'), 'token_clean_global' => make_secure_link_token($uri_clean_base . '/global'), 'token_clean_both' => make_secure_link_token($uri_clean_base . '/global+local'), 'global' => $global, 'config' => $config, 'mod' => $mod)); $reportHTML .= $content_html; } } if ($reportCount > 0 && $json) { array_walk($reports, function (&$v, $k, $ud) { $global = $ud['global']; $report_posts = $ud['report_posts']; $board = $v['board'] ? $v['board'] : NULL; if (isset($v['ip']) && !$global) { $v['ip'] = less_ip($v['ip'], $board ? $board : ''); } if (isset($report_posts[$v['board']][$v['post']])) { $post_content = $report_posts[$v['board']][$v['post']]; unset($post_content['password']); if (!$global) { $post_content['ip'] = less_ip($post_content['ip'], $board ? $board : ''); } $v['post_content'] = $post_content; } }, array('global' => $global, 'report_posts' => $report_posts)); } } $pageArgs = array('count' => $reportCount, 'reports' => $reportHTML, 'global' => $global); if ($json) { header('Content-Type: application/json'); echo json_encode($reports); } else { mod_page(sprintf('%s (%d)', _($global ? 'Global report queue' : 'Report queue'), $reportCount), 'mod/reports.html', $pageArgs); } }
if (!isset($postDatum['post_count'])) { $postDatum['post_count'] = 1; } else { ++$postDatum['post_count']; } // Add to post id array. if (!isset($postDatum['post_id_array'])) { $postDatum['post_id_array'] = array((int) $post['id']); } else { $postDatum['post_id_array'][] = (int) $post['id']; } // Add to ip array. if (!isset($postDatum['author_ip_array'])) { $postDatum['author_ip_array'] = array(); } $postDatum['author_ip_array'][less_ip($post['ip'])] = 1; unset($postHourTime); } // Prep data for insert. foreach ($postHour as $postHourTime => &$postHourData) { $postDatum =& $postHour[$postHourTime]; // Serialize arrays for TEXT insert. $postDatum['post_id_array'] = str_replace("\"", "\\\"", serialize($postDatum['post_id_array'])); $postDatum['author_ip_count'] = count(array_keys($postDatum['author_ip_array'])); $postDatum['author_ip_array'] = str_replace("\"", "\\\"", serialize(array_keys($postDatum['author_ip_array']))); } // Bash this shit together into a set of insert statements. $statsInserts = array(); foreach ($postHour as $postHourTime => $postHourData) { $statsInserts[] = "(\"{$board['uri']}\", \"{$postHourTime}\", \"{$postHourData['post_count']}\", \"{$postHourData['post_id_array']}\", \"{$postHourData['author_ip_count']}\", \"{$postHourData['author_ip_array']}\" )"; }