From faa51accaa860e543c5c2d97661925cb79eb606a Mon Sep 17 00:00:00 2001 From: 8chan Date: Fri, 21 Nov 2014 04:05:20 -0800 Subject: [PATCH] We dynamic pages now, read.php --- 404.php | 3 +- inc/config.php | 7 ++ inc/display.php | 8 +- inc/functions.php | 155 +++++++++++++++--------- inc/mod/pages.php | 3 + js/ajax.js | 4 +- js/thread-stats.js | 2 +- post.php | 6 +- read.php | 183 +++++++++++++++++++++++++++++ templates/themes/catalog/theme.php | 19 ++- 10 files changed, 323 insertions(+), 67 deletions(-) create mode 100644 read.php diff --git a/404.php b/404.php index 0e1b634f..cee2ece6 100644 --- a/404.php +++ b/404.php @@ -1,6 +1,7 @@ '. ''. ''; + + // Use read.php? + // read.php is a file that dynamically displays pages to users instead of the build on demand system in use in Tinyboard since 2010. + // + // read.php is basically a watered down mod.php -- if coupled with caching, it improves performance and allows for easier replication + // across machines. + $config['use_read_php'] = false; diff --git a/inc/display.php b/inc/display.php index a58d92f4..9561c4b8 100644 --- a/inc/display.php +++ b/inc/display.php @@ -406,9 +406,13 @@ class Post { } public function getClean( ) { - global $board; + global $board, $config; if( !isset( $this->clean ) ) { + if ($config['cache']['enabled'] && $this->clean = cache::get("post_clean_{$board['uri']}_{$this->id}")) { + return $this->clean; + } + $query = prepare("SELECT * FROM `post_clean` WHERE `post_id` = :post AND `board_id` = :board"); $query->bindValue( ':board', $board['uri'] ); $query->bindValue( ':post', $this->id ); @@ -424,6 +428,8 @@ class Post { 'clean_local_mod_id' => null, 'clean_global_mod_id' => null, ); + + cache::set("post_clean_{$board['uri']}_{$this->id}", $this->clean); } } diff --git a/inc/functions.php b/inc/functions.php index 2ffdc222..bf28009d 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1099,6 +1099,11 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { $antispam_query->bindValue(':board', $board['uri']); $antispam_query->bindValue(':thread', $post['id']); $antispam_query->execute() or error(db_error($antispam_query)); + + cache::delete("thread_index_{$board['uri']}_{$post['id']}"); + cache::delete("thread_index_display_{$board['uri']}_{$post['id']}"); + cache::delete("thread_{$board['uri']}_{$post['id']}"); + cache::delete("catalog_{$board['uri']}"); } elseif ($query->rowCount() == 1) { // Rebuild thread $rebuild = &$post['thread']; @@ -1190,57 +1195,62 @@ function index($page, $mod=false) { return false; $threads = array(); - + while ($th = $query->fetch(PDO::FETCH_ASSOC)) { - $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); + if ($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}"))) { + $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); - if ($config['cache']['enabled']) { - $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); - if (isset($cached['replies'], $cached['omitted'])) { - $replies = $cached['replies']; - $omitted = $cached['omitted']; - } else { - unset($cached); + if ($config['cache']['enabled']) { + $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); + if (isset($cached['replies'], $cached['omitted'])) { + $replies = $cached['replies']; + $omitted = $cached['omitted']; + } else { + unset($cached); + } } - } - if (!isset($cached)) { - $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); - $posts->bindValue(':id', $th['id']); - $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); - $posts->execute() or error(db_error($posts)); + if (!isset($cached)) { + $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); + $posts->bindValue(':id', $th['id']); + $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); + $posts->execute() or error(db_error($posts)); - $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); + $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); - if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { - $count = numPosts($th['id']); - $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); - } else { - $omitted = false; + if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { + $count = numPosts($th['id']); + $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); + } else { + $omitted = false; + } + + if ($config['cache']['enabled']) + cache::set("thread_index_{$board['uri']}_{$th['id']}", array( + 'replies' => $replies, + 'omitted' => $omitted, + )); } + $num_images = 0; + foreach ($replies as $po) { + if ($po['num_files']) + $num_images+=$po['num_files']; + + $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); + } + + $thread->images = $num_images; + $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); + + if ($omitted) { + $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); + $thread->omitted_images = $omitted['image_count'] - $num_images; + } + if ($config['cache']['enabled']) - cache::set("thread_index_{$board['uri']}_{$th['id']}", array( - 'replies' => $replies, - 'omitted' => $omitted, - )); + cache::set("thread_index_display_{$board['uri']}_{$th['id']}", $thread); + } - - $num_images = 0; - foreach ($replies as $po) { - if ($po['num_files']) - $num_images+=$po['num_files']; - - $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); - } - - $thread->images = $num_images; - $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); - - if ($omitted) { - $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); - $thread->omitted_images = $omitted['image_count'] - $num_images; - } - $threads[] = $thread; $body .= $thread->build(true); } @@ -1458,6 +1468,10 @@ function checkMute() { function buildIndex() { global $board, $config, $build_pages; + if ($config['use_read_php']) { + cache::delete("catalog_{$board['uri']}"); + return; + } $pages = getPages(); if (!$config['try_smarter']) @@ -1535,6 +1549,17 @@ function buildIndex() { function buildJavascript() { global $config; + if ($config['cache']['enabled']) { + if (false === strpos($config['file_script'], '/')) { + $cache_name = 'main_js'; + } else { + $b = explode('/', $config['file_script'])[0]; + $cache_name = "board_{$b}_js"; + } + + cache::delete($cache_name); + } + $script = Element('main.js', array( 'config' => $config, )); @@ -1556,7 +1581,14 @@ function buildJavascript() { $script = JSMin::minify($script); } - file_write($config['file_script'], $script); + if ($config['cache']['enabled']) + cache::set($cache_name, $script); + + if (!$config['use_read_php']) { + file_write($config['file_script'], $script); + } else { + return $script; + } } function checkDNSBL() { @@ -1990,27 +2022,34 @@ function strip_combining_chars($str) { function buildThread($id, $return = false, $mod = false) { global $board, $config, $build_pages; + if (!$return && $config['use_read_php']) { + cache::delete("thread_index_{$board['uri']}_{$id}"); + cache::delete("thread_index_display_{$board['uri']}_{$id}"); + cache::delete("thread_{$board['uri']}_{$id}"); + cache::delete("catalog_{$board['uri']}"); + return; + } + $id = round($id); if (event('build-thread', $id)) return; - if ($config['cache']['enabled'] && !$mod) { - // Clear cache - cache::delete("thread_index_{$board['uri']}_{$id}"); - cache::delete("thread_{$board['uri']}_{$id}"); - } + if (!($thread = cache::get("thread_{$board['uri']}_{$id}"))) { + unset($thread); + $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); + $query->bindValue(':id', $id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); - $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); - $query->bindValue(':id', $id, PDO::PARAM_INT); - $query->execute() or error(db_error($query)); - - while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - if (!isset($thread)) { - $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); - } else { - $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + if (!isset($thread)) { + $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); + } else { + $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod)); + } } + + if (isset($thread)) cache::set("thread_{$board['uri']}_{$id}", $thread); } // Check if any posts were found @@ -2038,7 +2077,7 @@ function buildThread($id, $return = false, $mod = false) { $build_pages[] = thread_find_page($id); // json api - if ($config['api']['enabled']) { + if ($config['api']['enabled'] && !$config['use_read_php']) { $api = new Api(); $json = json_encode($api->translateThread($thread)); $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 61920b46..0f059f05 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -2995,6 +2995,9 @@ function mod_report_clean( $global_reports, $board, $unclean, $post, $global, $l $log_action = ($unclean ? "Closed" : "Re-opened" ); $log_scope = ($local && $global ? "local and global" : ($local ? "local" : "global" ) ); modLog( "{$log_action} reports for post #{$post} in {$log_scope}.", $board); + if ($config['cache']['enabled']) { + cache::delete("post_clean_{$board}_{$post}"); + } rebuildPost( $post ); } diff --git a/js/ajax.js b/js/ajax.js index 1b2bf2c0..a80ae541 100644 --- a/js/ajax.js +++ b/js/ajax.js @@ -55,7 +55,7 @@ $(window).ready(function() { } return xhr; }, - success: function(post_response) { + success: function(post_response, textStatus, xhr) { if (post_response.error) { if (post_response.banned) { // You are banned. Must post the form normally so the user can see the ban message. @@ -109,12 +109,14 @@ $(window).ready(function() { $(form).find('input[type="submit"]').val(_('Posted...')); $(document).trigger("ajax_after_post", post_response); } else { + console.log(xhr); alert(_('An unknown error occured when posting!')); $(form).find('input[type="submit"]').val(submit_txt); $(form).find('input[type="submit"]').removeAttr('disabled'); } }, error: function(xhr, status, er) { + console.log(xhr); alert(_('The server returned an error or truncated response -- your post was probably still submitted. If it wasn\'t, 8chan.co might be experiencing issues right now -- please try your post again later.')); }, data: formData, diff --git a/js/thread-stats.js b/js/thread-stats.js index 7c13d66c..52176ae8 100644 --- a/js/thread-stats.js +++ b/js/thread-stats.js @@ -1,4 +1,4 @@ -/* +/* * thread-stats.js * - Adds statistics of the thread below the posts area * - Shows ID post count beside each postID on hover diff --git a/post.php b/post.php index bffc91c0..50c5c8bd 100644 --- a/post.php +++ b/post.php @@ -852,8 +852,10 @@ elseif (isset($_POST['post'])) { bumpThread($post['thread']); } - buildThread($post['op'] ? $id : $post['thread']); - + $pid = $post['op'] ? $id : $post['thread']; + + buildThread($pid); + if ($config['try_smarter'] && $post['op']) $build_pages = range(1, $config['max_pages']); diff --git a/read.php b/read.php new file mode 100644 index 00000000..f1589ee3 --- /dev/null +++ b/read.php @@ -0,0 +1,183 @@ + 'view_board', + '/(\%b)/(\d+)\.json' => 'view_api_index', + '/(\%b)/catalog\.json' => 'view_api_catalog', + '/(\%b)/threads\.json' => 'view_api_threads', + '/(\%b)/main\.js' => 'view_js', + '/main\.js' => 'view_js', + '/(\%b)/catalog(\.html)?' => 'view_catalog', + '/(\%b)/' . preg_quote($config['file_index'], '!') => 'view_board', + '/(\%b)/' . str_replace('%d', '(\d+)', preg_quote($config['file_page'], '!')) => 'view_board', + '/(\%b)/' . preg_quote($config['dir']['res'], '!') . + str_replace('%d', '(\d+)', preg_quote($config['file_page50'], '!')) => 'view_thread50', + '/(\%b)/' . preg_quote($config['dir']['res'], '!') . + str_replace('%d', '(\d+)', '%d(\.html)?') => 'view_thread', +); + +$new_pages = array(); +foreach ($pages as $key => $callback) { + if (is_string($callback) && preg_match('/^secure /', $callback)) + $key .= '(/(?P[a-f0-9]{8}))?'; + $key = str_replace('\%b', '?P' . sprintf(substr($config['board_path'], 0, -1), $config['board_regex']), $key); + $new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!u'] = $callback; +} +$pages = $new_pages; + +function view_thread50($boardName, $thread) { + global $config, $mod; + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + $page = buildThread50($thread, true, false); + echo $page; +} + +function view_thread($boardName, $thread) { + global $config, $mod; + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + $page = buildThread($thread, true, false); + echo $page; +} + +function view_api_index($boardName, $page) { + global $config, $board; + + $api = new Api(); + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + $content = index($page+1); + + if (!$content) + return; + + if ($config['api']['enabled']) { + $threads = $content['threads']; + $json = json_encode($api->translatePage($threads)); + header('Content-Type: text/json'); + + echo $json; + } +} + +function APICatalog($boardName, $gen_threads = false) { + global $config; + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + header('Content-Type: text/json'); + + $catalog = array(); + $api = new Api(); + + for ($page = 1; $page <= $config['max_pages']; $page++) { + $content = index($page); + + if (!$content) + break; + + $catalog[$page-1] = $content['threads']; + } + + echo json_encode($api->translateCatalog($catalog, $gen_threads)); +} + +function view_api_catalog($boardName) { + APICatalog($boardName, false); +} + +function view_api_threads($boardName) { + APICatalog($boardName, true); +} + +function view_board($boardName, $page_no = 1) { + global $config, $mod; + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + if (!$page = index($page_no, $mod)) { + error($config['error']['404']); + } + + $page['pages'] = getPages(false); + $page['pages'][$page_no-1]['selected'] = true; + $page['btn'] = getPageButtons($page['pages'], false); + $page['mod'] = false; + $page['config'] = $config; + + echo Element('index.html', $page); +} + +function view_js($boardName = false) { + global $config; + + if ($boardName && !openBoard($boardName)) + error($config['error']['noboard']); + + if (!$boardName) { + $cache_name = 'main_js'; + } else { + $cache_name = "board_{$boardName}_js"; + } + + if (!($script = cache::get($cache_name))) { + $script = buildJavascript(); + } + + echo $script; +} + +function view_catalog($boardName) { + global $board, $config; + $_theme = 'catalog'; + + $theme = loadThemeConfig($_theme); + + if (!openBoard($boardName)) { + include '404.php'; + return; + } + + require_once $config['dir']['themes'] . '/' . $_theme . '/theme.php'; + + $catalog = $theme['build_function']('read_php', themeSettings($_theme), $board['uri']); + echo $catalog; +} + +$found = false; +foreach ($pages as $uri => $handler) { + if (preg_match($uri, $query, $matches)) { + $matches = array_slice($matches, 2); + if (is_callable($handler)) { + $found = true; + call_user_func_array($handler, $matches); + } + } +} + +if (!$found) + include '404.php'; diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 5b940167..72e43c14 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -25,14 +25,18 @@ } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { $b = new Catalog(); $b->build($settings, $board); + } elseif ($action == 'read_php') { + $b = new Catalog(); + return $b->build($settings, $board, true); } } // Wrap functions in a class so they don't interfere with normal Tinyboard operations class Catalog { - public function build($settings, $board_name) { + public function build($settings, $board_name, $return = false) { global $config, $board; + if (!($config['cache']['enabled'] && $catalog_out = cache::get("catalog_{$board['uri']}"))) {; openBoard($board_name); $recent_images = array(); @@ -90,7 +94,7 @@ $config['additional_javascript'][] = $s; } - file_write($config['dir']['home'] . $board_name . '/catalog.html', Element('themes/catalog/catalog.html', Array( + $catalog_out = Element('themes/catalog/catalog.html', Array( 'settings' => $settings, 'config' => $config, 'boardlist' => createBoardlist(), @@ -99,6 +103,15 @@ 'stats' => $stats, 'board' => $board_name, 'link' => $config['root'] . $board['dir'] - ))); + )); + + cache::set("catalog_{$board['uri']}", $catalog_out); + } + + if ($return) { + return $catalog_out; + } else { + file_write($config['dir']['home'] . $board_name . '/catalog.html', $catalog_out); + } } };