build()); file_write($settings['uri'] . '/semirand.js', Element('themes/semirand/semirand.js', array())); } } /** * Encapsulation of the theme's internals */ class semirand { private $settings; function __construct($settings) { $this->settings = $this->parseSettings($settings); } /** * Parse and validate configuration parameters passed from the UI */ private function parseSettings($settings) { if (!is_numeric($settings['thread_limit']) || !is_numeric($settings['random_count']) || !is_numeric($settings['recent_count'])) { error('Invalid configuration parameters.', true); } $settings['exclude'] = explode(' ', $settings['exclude']); $settings['thread_limit'] = intval($settings['thread_limit']); $settings['random_count'] = intval($settings['random_count']); $settings['recent_count'] = intval($settings['recent_count']); if ($settings['thread_limit'] < 1 || $settings['random_count'] < 1 || $settings['recent_count'] < 1) { error('Invalid configuration parameters.', true); } return $settings; } /** * Obtain list of all threads from all non-excluded boards */ private function fetchThreads() { $query = ''; $boards = listBoards(true); foreach ($boards as $b) { if (in_array($b, $this->settings['exclude'])) continue; // Threads are those posts that have no parent thread $query .= "SELECT *, '$b' AS `board` FROM ``posts_$b`` " . "WHERE `thread` IS NULL UNION ALL "; } $query = preg_replace('/UNION ALL $/', 'ORDER BY `bump` DESC', $query); $result = query($query) or error(db_error()); return $result->fetchAll(PDO::FETCH_ASSOC); } /** * Retrieve all replies to a given thread */ private function fetchReplies($board, $thread_id) { $query = prepare("SELECT * FROM ``posts_$board`` WHERE `thread` = :id"); $query->bindValue(':id', $thread_id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); return $query->fetchAll(PDO::FETCH_ASSOC); } /** * Intersperse random threads between those in bump order */ private function shuffleThreads($threads) { $random_count = $this->settings['random_count']; $recent_count = $this->settings['recent_count']; $total = count($threads); // Storage for threads that will be randomly interspersed $shuffled = array(); // Ratio of bumped / all threads $topRatio = $recent_count / ($recent_count + $random_count); // Shuffle the bottom half of all threads $random = array_splice($threads, floor($total * $topRatio)); shuffle($random); // Merge the random and sorted threads into one sequence. The pattern // starts at random threads while (!empty($threads)) { $shuffled = array_merge($shuffled, array_splice($random, 0, $random_count), array_splice($threads, 0, $recent_count)); } return $shuffled; } /** * Build the HTML of a single thread in the catalog */ private function buildOne($post, $mod = false) { global $config; openBoard($post['board']); $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); $replies = $this->fetchReplies($post['board'], $post['id']); // Number of replies to a thread that are displayed beneath it $preview_count = $post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']; // Chomp the last few replies $disp_replies = array_splice($replies, 0, $preview_count); $disp_img_count = 0; foreach ($disp_replies as $reply) { if ($reply['files'] !== '') ++$disp_img_count; // Append the reply to the thread as it's being built $thread->add(new Post($reply, $mod ? '?/' : $config['root'], $mod)); } // Count the number of omitted image replies $omitted_img_count = count(array_filter($replies, function($p) { return $p['files'] !== ''; })); // Set the corresponding omitted numbers on the thread if (!empty($replies)) { $thread->omitted = count($replies); $thread->omitted_images = $omitted_img_count; } // Board name and link $html = '