From 54012e2ee3a5ac7783727fc9a03ab9db5cb77dd2 Mon Sep 17 00:00:00 2001 From: unknown <8n-tech@users.noreply.github.com> Date: Thu, 23 Oct 2014 05:07:54 -0500 Subject: [PATCH 01/70] [#201] Fixed edit text margins. --- inc/display.php | 2 ++ stylesheets/style.css | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/inc/display.php b/inc/display.php index 9b4c2d83..f4cea4e5 100644 --- a/inc/display.php +++ b/inc/display.php @@ -345,6 +345,8 @@ function embed_html($link) { class Post { + public $clean; + public function __construct($post, $root=null, $mod=false) { global $config; if (!isset($root)) diff --git a/stylesheets/style.css b/stylesheets/style.css index 0a3ed8eb..062b2731 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -291,14 +291,15 @@ div.post.reply { div.post_modified { min-width: 47.5em; margin-left: 1.8em; - padding-top: 0.8em; } - div.post_modified div.content-status { margin-top: 0.5em; padding-bottom: 0em; font-size: 72%; } +div.post_modified div.content-status:first-child { + margin-top: 1.3em; +} span.trip { color: #228854; From 08fd6ae474adfc4411346f222c150cf7f305f7a8 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 16:53:28 +0200 Subject: [PATCH 02/70] don`t rebuild a page, when not needed, even if it doesn`t exist --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 8674ecd0..86389e73 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1507,7 +1507,7 @@ function buildIndex() { $filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page)); if (!$config['api']['enabled'] && $config['try_smarter'] && isset($build_pages) && !empty($build_pages) - && !in_array($page, $build_pages) && is_file($filename)) + && !in_array($page, $build_pages) ) continue; $content = index($page); if (!$content) @@ -1524,7 +1524,7 @@ function buildIndex() { } if ($config['api']['enabled'] && $config['try_smarter'] && isset($build_pages) && !empty($build_pages) - && !in_array($page, $build_pages) && is_file($filename)) + && !in_array($page, $build_pages) ) continue; if ($config['try_smarter']) { From 453ecfdc6b665c857a25b2bb4063164bd99ee914 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 16:56:17 +0200 Subject: [PATCH 03/70] unlink a .gz version of a file if it exists --- inc/functions.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/inc/functions.php b/inc/functions.php index 86389e73..68112c0e 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -630,6 +630,13 @@ function file_unlink($path) { } $ret = @unlink($path); + + if ($config['gzip_static']) { + $gzpath = "$path.gz"; + + @unlink($gzpath); + } + if (isset($config['purge']) && $path[0] != '/' && isset($_SERVER['HTTP_HOST'])) { // Purge cache if (basename($path) == $config['file_index']) { From 3f381ee569425ad7276ca94739abd15897bbaa70 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 17:07:24 +0200 Subject: [PATCH 04/70] add a global_api variable for buildIndex --- inc/functions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 68112c0e..5a74bc09 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1498,7 +1498,7 @@ function checkMute() { } } -function buildIndex() { +function buildIndex($global_api = "yes") { global $board, $config, $build_pages; $pages = getPages(); @@ -1513,8 +1513,8 @@ function buildIndex() { for ($page = 1; $page <= $config['max_pages']; $page++) { $filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page)); - if (!$config['api']['enabled'] && $config['try_smarter'] && isset($build_pages) && !empty($build_pages) - && !in_array($page, $build_pages) ) + if (!$config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages) + && !empty($build_pages) && !in_array($page, $build_pages) ) continue; $content = index($page); if (!$content) @@ -1530,8 +1530,8 @@ function buildIndex() { $catalog[$page-1] = $threads; } - if ($config['api']['enabled'] && $config['try_smarter'] && isset($build_pages) && !empty($build_pages) - && !in_array($page, $build_pages) ) + if ($config['api']['enabled'] && $global_api == "skip" && $config['try_smarter'] && isset($build_pages) + && !empty($build_pages) && !in_array($page, $build_pages) ) continue; if ($config['try_smarter']) { @@ -1560,7 +1560,7 @@ function buildIndex() { } // json api catalog - if ($config['api']['enabled']) { + if ($config['api']['enabled'] && $global_api != "skip") { $json = json_encode($api->translateCatalog($catalog)); $jsonFilename = $board['dir'] . 'catalog.json'; file_write($jsonFilename, $json); From b2f6c4c9568cd0571ecbb8d91a1ae2139e00f64c Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 17:16:30 +0200 Subject: [PATCH 05/70] smart build: define configuration variables --- inc/config.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index b8e7617d..6244479f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1239,9 +1239,20 @@ // Website favicon. $config['url_favicon'] = 'static/favicon.ico'; - // EXPERIMENTAL: Try not to build pages when we shouldn't have to. + // Try not to build pages when we shouldn't have to. $config['try_smarter'] = true; + // EXPERIMENTAL: Defer static HTML building to a moment, when a given file is actually accessed. + // Warning: This option won't run out of the box. You need to tell your webserver, that a file + // for serving 403 and 404 pages is /smart_build.php. Also, you need to turn off indexes. + $config['smart_build'] = false; + + // Smart build related: when a file doesn't exist, where should we redirect? + $config['page_404'] = '/404.html'; + + // Smart build related: extra entrypoints. + $config['smart_build_entrypoints'] = array(); + /* * ==================== * Mod settings From 854b9689b305cbd23ed4abdaf24de143d5607a53 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 17:30:06 +0200 Subject: [PATCH 06/70] fix one of the previous commits: fix api_global --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 5a74bc09..2473ed7c 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1513,7 +1513,7 @@ function buildIndex($global_api = "yes") { for ($page = 1; $page <= $config['max_pages']; $page++) { $filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page)); - if (!$config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages) + if ((!$config['api']['enabled'] || $global_api == "skip") && $config['try_smarter'] && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) ) continue; $content = index($page); @@ -1530,7 +1530,7 @@ function buildIndex($global_api = "yes") { $catalog[$page-1] = $threads; } - if ($config['api']['enabled'] && $global_api == "skip" && $config['try_smarter'] && isset($build_pages) + if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) ) continue; From 1e5fb18f37c66fd7d14285312a08155384d404b7 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 17:50:59 +0200 Subject: [PATCH 07/70] smart_build.php: initial release --- smart_build.php | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 smart_build.php diff --git a/smart_build.php b/smart_build.php new file mode 100644 index 00000000..6621238e --- /dev/null +++ b/smart_build.php @@ -0,0 +1,139 @@ + $config['max_pages']) return false; + $config['try_smarter'] = true; + $build_pages = array($page); + buildIndex("skip"); + return true; +} + +function sb_api_board($b, $page = 0) { $page = (int)$page; + return sb_board($b, $page + 1); +} + +function sb_thread($b, $thread) { global $config; $thread = (int)$thread; + if (!openBoard($b)) return false; + buildThread($thread); + return true; +} + +function sb_api($b) { global $config; + if (!openBoard($b)) return false; + $config['try_smarter'] = true; + $build_pages = array(-1); + buildIndex(); + return true; +} + +function sb_ukko() { + rebuildTheme("ukko", "post-thread"); + return true; +} + +function sb_catalog($b) { + rebuildTheme("catalog", "post-thread", $b); + return true; +} + +function sb_recent() { + rebuildTheme("recent", "post-thread"); + return true; +} + +$entrypoints = array(); + +$entrypoints['/%b/'] = 'sb_board'; +$entrypoints['/%b/'.$config['file_index']] = 'sb_board'; +$entrypoints['/%b/'.$config['file_page']] = 'sb_board'; +$entrypoints['/%b/%d.json'] = 'sb_api_board'; +if ($config['api']['enabled']) { + $entrypoints['/%b/threads.json'] = 'sb_api'; + $entrypoints['/%b/catalog.json'] = 'sb_api'; +} + +$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread'; +$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread'; +if ($config['slugify']) { + $entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread'; + $entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread'; +} +if ($config['api']['enabled']) { + $entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread'; +} + +$entrypoints['/*/'] = 'sb_ukko'; +$entrypoints['/*/index.html'] = 'sb_ukko'; +$entrypoints['/recent.html'] = 'sb_recent'; +$entrypoints['/%b/catalog.html'] = 'sb_catalog'; + +$reached = false; + +$request = $_SERVER['REQUEST_URI']; + +foreach ($entrypoints as $id => $fun) { + $id = '@^' . preg_quote($id, '@') . '$@u'; + + $id = str_replace('%b', '('.$config['board_regex'].')', $id); + $id = str_replace('%d', '([0-9]+)', $id); + $id = str_replace('%s', '[a-zA-Z0-9-]+', $id); + + $matches = null; + + if (preg_match ($id, $request, $matches)) { + array_shift($matches); + + $reached = call_user_func_array($fun, $matches); + + break; + } +} + +if ($reached) { + if ($request[strlen($request)-1] == '/') { + $request .= 'index.html'; + } + $request = '.'.$request; + + if (!file_exists($request)) { + header("Location: ".$config['page_404']); + die(); + } + + header("HTTP/1.1 200 OK"); + header("Status: 200 OK"); + if (preg_match('/\.json$/', $request)) { + header("Content-Type", "application/json"); + } + elseif (preg_match('/\.js$/', $request)) { + header("Content-Type", "text/javascript; charset=utf-8"); + } + else { + header("Content-Type", "text/html; charset=utf-8"); + } + header("Cache-Control: public, nocache, no-cache, max-age=0, must-revalidate"); + header("Expires: Fri, 22 Feb 1991 06:00:00 GMT"); + header("Last-Modified: ".date('r', filemtime($request))); + + //if (isset ($_SERVER['HTTP_ACCEPT_ENCODING']) && preg_match('/gzip/', $_SERVER['HTTP_ACCEPT_ENCODING']) && file_exists($request.".gz")) { + // header("Content-Encoding: gzip"); + // $file = fopen($request.".gz", 'r'); + //} + //else { + $file = fopen($request, 'r'); + //} + fpassthru($file); + fclose($file); +} +else { + header("Location: ".$config['page_404']); +} From 97e0380d658e89015cfd5048559bf71b025c50f5 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 18:11:08 +0200 Subject: [PATCH 08/70] smart_build for buildIndex --- inc/functions.php | 101 +++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 2473ed7c..830b9761 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1501,53 +1501,62 @@ function checkMute() { function buildIndex($global_api = "yes") { global $board, $config, $build_pages; - $pages = getPages(); - if (!$config['try_smarter']) - $antibot = create_antibot($board['uri']); + if (!$config['smart_build']) { + $pages = getPages(); + if (!$config['try_smarter']) + $antibot = create_antibot($board['uri']); - if ($config['api']['enabled']) { - $api = new Api(); - $catalog = array(); + if ($config['api']['enabled']) { + $api = new Api(); + $catalog = array(); + } } for ($page = 1; $page <= $config['max_pages']; $page++) { $filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page)); + $jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0 - if ((!$config['api']['enabled'] || $global_api == "skip") && $config['try_smarter'] && isset($build_pages) - && !empty($build_pages) && !in_array($page, $build_pages) ) - continue; - $content = index($page); - if (!$content) - break; - - // json api - if ($config['api']['enabled']) { - $threads = $content['threads']; - $json = json_encode($api->translatePage($threads)); - $jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0 - file_write($jsonFilename, $json); - - $catalog[$page-1] = $threads; - } - - if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages) - && !empty($build_pages) && !in_array($page, $build_pages) ) + if ((!$config['api']['enabled'] || $global_api == "skip" || $config['smart_build']) && $config['try_smarter'] + && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) ) continue; - if ($config['try_smarter']) { - $antibot = create_antibot($board['uri'], 0 - $page); - $content['current_page'] = $page; - } - $antibot->reset(); - $content['pages'] = $pages; - $content['pages'][$page-1]['selected'] = true; - $content['btn'] = getPageButtons($content['pages']); - $content['antibot'] = $antibot; + if (!$config['smart_build']) { + $content = index($page); + if (!$content) + break; - file_write($filename, Element('index.html', $content)); + // json api + if ($config['api']['enabled']) { + $threads = $content['threads']; + $json = json_encode($api->translatePage($threads)); + file_write($jsonFilename, $json); + + $catalog[$page-1] = $threads; + } + + if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages) + && !empty($build_pages) && !in_array($page, $build_pages) ) + continue; + + if ($config['try_smarter']) { + $antibot = create_antibot($board['uri'], 0 - $page); + $content['current_page'] = $page; + } + $antibot->reset(); + $content['pages'] = $pages; + $content['pages'][$page-1]['selected'] = true; + $content['btn'] = getPageButtons($content['pages']); + $content['antibot'] = $antibot; + + file_write($filename, Element('index.html', $content)); + } + else { + file_unlink($filename); + file_unlink($jsonFilename); + } } - if ($page < $config['max_pages']) { + if (!$config['smart_build'] && $page < $config['max_pages']) { for (;$page<=$config['max_pages'];$page++) { $filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page)); file_unlink($filename); @@ -1561,13 +1570,21 @@ function buildIndex($global_api = "yes") { // json api catalog if ($config['api']['enabled'] && $global_api != "skip") { - $json = json_encode($api->translateCatalog($catalog)); - $jsonFilename = $board['dir'] . 'catalog.json'; - file_write($jsonFilename, $json); + if ($config['smart_build']) { + $jsonFilename = $board['dir'] . 'catalog.json'; + file_unlink($jsonFilename); + $jsonFilename = $board['dir'] . 'threads.json'; + file_unlink($jsonFilename); + } + else { + $json = json_encode($api->translateCatalog($catalog)); + $jsonFilename = $board['dir'] . 'catalog.json'; + file_write($jsonFilename, $json); - $json = json_encode($api->translateCatalog($catalog, true)); - $jsonFilename = $board['dir'] . 'threads.json'; - file_write($jsonFilename, $json); + $json = json_encode($api->translateCatalog($catalog, true)); + $jsonFilename = $board['dir'] . 'threads.json'; + file_write($jsonFilename, $json); + } } if ($config['try_smarter']) From d14594ce6384e7f07b56b941523fdc350e5dcac1 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 18:13:32 +0200 Subject: [PATCH 09/70] functions.php fix: after_open_board support; so that we may disable smart_build immediately after open_board --- inc/functions.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/inc/functions.php b/inc/functions.php index 830b9761..9cafa9a7 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -469,6 +469,11 @@ function openBoard($uri) { $board = getBoardInfo($uri); if ($board) { setupBoard($board); + + if (function_exists('after_open_board')) { + after_open_board(); + } + return true; } return false; From 66638348dc446e73b088224ff4f2c99a23292cfd Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 18:43:48 +0200 Subject: [PATCH 10/70] smart_build: buildThread --- inc/functions.php | 89 ++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 9cafa9a7..0adade57 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2078,51 +2078,62 @@ function buildThread($id, $return = false, $mod = false) { cache::delete("thread_{$board['uri']}_{$id}"); } - $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)); + if (!$config['smart_build'] || $return || $mod) { + $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)); + } + } + + // Check if any posts were found + if (!isset($thread)) + error($config['error']['nonexistant']); + + $hasnoko50 = $thread->postCount() >= $config['noko50_min']; + $antibot = $mod || $return ? false : create_antibot($board['uri'], $id); + + $body = Element('thread.html', array( + 'board' => $board, + 'thread' => $thread, + 'body' => $thread->build(), + 'config' => $config, + 'id' => $id, + 'mod' => $mod, + 'hasnoko50' => $hasnoko50, + 'isnoko50' => false, + 'antibot' => $antibot, + 'boardlist' => createBoardlist($mod), + 'return' => ($mod ? '?' . $board['url'] . $config['file_index'] : $config['root'] . $board['dir'] . $config['file_index']) + )); + + if ($config['try_smarter'] && !$mod) + $build_pages[] = thread_find_page($id); + + // json api + if ($config['api']['enabled']) { + $api = new Api(); + $json = json_encode($api->translateThread($thread)); + $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; + file_write($jsonFilename, $json); } } - - // Check if any posts were found - if (!isset($thread)) - error($config['error']['nonexistant']); - - $hasnoko50 = $thread->postCount() >= $config['noko50_min']; - $antibot = $mod || $return ? false : create_antibot($board['uri'], $id); - - $body = Element('thread.html', array( - 'board' => $board, - 'thread' => $thread, - 'body' => $thread->build(), - 'config' => $config, - 'id' => $id, - 'mod' => $mod, - 'hasnoko50' => $hasnoko50, - 'isnoko50' => false, - 'antibot' => $antibot, - 'boardlist' => createBoardlist($mod), - 'return' => ($mod ? '?' . $board['url'] . $config['file_index'] : $config['root'] . $board['dir'] . $config['file_index']) - )); - - if ($config['try_smarter'] && !$mod) - $build_pages[] = thread_find_page($id); - - // json api - if ($config['api']['enabled']) { - $api = new Api(); - $json = json_encode($api->translateThread($thread)); + else { $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; - file_write($jsonFilename, $json); + file_unlink($jsonFilename); } - if ($return) { + if ($config['smart_build'] && !$return && !$mod) { + $noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true); + file_unlink($noko50fn); + + file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id))); + } else if ($return) { return $body; } else { $noko50fn = $board['dir'] . $config['dir']['res'] . sprintf($config['file_page50'], $id); From 4244f2cad36d1a9760e16b4b1f7088f981156296 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 1 Apr 2015 18:46:48 +0200 Subject: [PATCH 11/70] smart_build.php: misc fixes --- smart_build.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/smart_build.php b/smart_build.php index 6621238e..d937241b 100644 --- a/smart_build.php +++ b/smart_build.php @@ -7,8 +7,12 @@ if (!$config['smart_build']) { $config['smart_build'] = false; // Let's disable it, so we can build the page for real +function after_open_board() { global $config; + $config['smart_build'] = false; +}; + function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page; - if ($page < 1 && $page != -1) return false; + if ($page < 1) return false; if (!openBoard($b)) return false; if ($page > $config['max_pages']) return false; $config['try_smarter'] = true; @@ -46,7 +50,7 @@ function sb_catalog($b) { } function sb_recent() { - rebuildTheme("recent", "post-thread"); + rebuildTheme("recent", "post-thread"); return true; } From 7581fc3094b2df85dc3342360b7ce043393be058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20=C5=81abanowski?= Date: Thu, 2 Apr 2015 20:27:10 +0200 Subject: [PATCH 12/70] smart_build.php: optimizations, guards, also a functionality to correct slugs --- smart_build.php | 68 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/smart_build.php b/smart_build.php index d937241b..56299aee 100644 --- a/smart_build.php +++ b/smart_build.php @@ -25,12 +25,53 @@ function sb_api_board($b, $page = 0) { $page = (int)$page; return sb_board($b, $page + 1); } -function sb_thread($b, $thread) { global $config; $thread = (int)$thread; +function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread; + if ($thread < 1) return false; + + if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false; + + if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false; + + $query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b)); + if (!$query->execute()) return false; + + $s = $query->fetch(PDO::FETCH_ASSOC); + $max = $s['max']; + + if ($thread > $max) return false; + + $query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b)); + $query->bindValue(':id', $thread); + + if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) { + Cache::set("thread_exists_".$b."_".$thread, "no"); + return false; + } + + if ($slugcheck && $config['slugify']) { + global $request; + + $link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b)); + $link = "/".$b."/".$config['dir']['res'].$link; + + if ($link != $request) { + header("Location: $link", true, 301); + die(); + } + } + if (!openBoard($b)) return false; buildThread($thread); return true; } +function sb_thread_slugcheck($b, $thread) { + return sb_thread($b, $thread, true); +} +function sb_thread_slugcheck50($b, $thread) { + return sb_thread($b, $thread, 50); +} + function sb_api($b) { global $config; if (!openBoard($b)) return false; $config['try_smarter'] = true; @@ -65,11 +106,11 @@ if ($config['api']['enabled']) { $entrypoints['/%b/catalog.json'] = 'sb_api'; } -$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread'; -$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread'; +$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck'; +$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50'; if ($config['slugify']) { - $entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread'; - $entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread'; + $entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck'; + $entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50'; } if ($config['api']['enabled']) { $entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread'; @@ -102,6 +143,18 @@ foreach ($entrypoints as $id => $fun) { } } +function die_404() { global $config; + if (!$config['page_404']) { + header("HTTP/1.1 404 Not Exists"); + header("Status: 404 Not Exists"); + echo "

404 Not Exists

Page doesn't exist


vichan
"; + } + else { + header("Location: ".$config['page_404']); + } + die(); +} + if ($reached) { if ($request[strlen($request)-1] == '/') { $request .= 'index.html'; @@ -109,8 +162,7 @@ if ($reached) { $request = '.'.$request; if (!file_exists($request)) { - header("Location: ".$config['page_404']); - die(); + die_404(); } header("HTTP/1.1 200 OK"); @@ -139,5 +191,5 @@ if ($reached) { fclose($file); } else { - header("Location: ".$config['page_404']); + die_404(); } From 076bd22586d61a0000b09296d65ba3c9ecff6e0e Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 2 Apr 2015 20:35:17 +0200 Subject: [PATCH 13/70] smart_build.php: additional fixes --- smart_build.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/smart_build.php b/smart_build.php index 56299aee..d8352621 100644 --- a/smart_build.php +++ b/smart_build.php @@ -124,6 +124,7 @@ $entrypoints['/%b/catalog.html'] = 'sb_catalog'; $reached = false; $request = $_SERVER['REQUEST_URI']; +list($request) = explode('?', $request); foreach ($entrypoints as $id => $fun) { $id = '@^' . preg_quote($id, '@') . '$@u'; @@ -145,13 +146,14 @@ foreach ($entrypoints as $id => $fun) { function die_404() { global $config; if (!$config['page_404']) { - header("HTTP/1.1 404 Not Exists"); - header("Status: 404 Not Exists"); - echo "

404 Not Exists

Page doesn't exist


vichan
"; + header("HTTP/1.1 404 Not Found"); + header("Status: 404 Not Found"); + echo "

404 Not Found

Page doesn't exist


vichan
"; } else { header("Location: ".$config['page_404']); } + die(); } From 0c3c982d7a7771e93d133405a519331dc59e8ddd Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 2 Apr 2015 20:43:06 +0200 Subject: [PATCH 14/70] smart_build.php: add a protection for +50; maybe we don`t need to rebuild after all? --- smart_build.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/smart_build.php b/smart_build.php index d8352621..dd9450ae 100644 --- a/smart_build.php +++ b/smart_build.php @@ -59,6 +59,14 @@ function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = die(); } } + + if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway + global $request; + $r = str_replace("+50", "", $request); + $r = substr($r, 1); // Cut the slash + + if (file_exists($r)) return false; + } if (!openBoard($b)) return false; buildThread($thread); From 402dd58a4bf4ab6f4a67ba41d52631c4da5bbac4 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 2 Apr 2015 20:53:50 +0200 Subject: [PATCH 15/70] smart_build.php: a small fix to catalog handling --- smart_build.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smart_build.php b/smart_build.php index dd9450ae..fb467233 100644 --- a/smart_build.php +++ b/smart_build.php @@ -94,6 +94,8 @@ function sb_ukko() { } function sb_catalog($b) { + if (!openBoard($b)) return false; + rebuildTheme("catalog", "post-thread", $b); return true; } From 36b1714e42c905430daeada1c4174fb118832afd Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 2 Apr 2015 20:54:28 +0200 Subject: [PATCH 16/70] themes can use smart_build now --- templates/themes/catalog/theme.php | 25 +++++++++++++++++++++---- templates/themes/recent/theme.php | 10 ++++++++-- templates/themes/ukko/theme.php | 10 +++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index a7dfabd9..42fad1ea 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -20,12 +20,25 @@ if ($action == 'all') { foreach ($boards as $board) { $b = new Catalog(); - $b->build($settings, $board); + + if ($config['smart_build']) { + file_unlink($config['dir']['home'] . $board . '/catalog.html'); + } + else { + $b->build($settings, $board); + } + if (php_sapi_name() === "cli") echo "Rebuilding $board catalog...\n"; } } 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); + + if ($config['smart_build']) { + file_unlink($config['dir']['home'] . $board . '/catalog.html'); + } + else { + $b->build($settings, $board); + } } } @@ -33,8 +46,12 @@ class Catalog { public function build($settings, $board_name) { global $config, $board; - - openBoard($board_name); + + if ($board['uri'] != $board_name) { + if (!openBoard($board_name)) { + error(sprintf(_("Board %s doesn't exist"), $board_name)); + } + } $recent_images = array(); $recent_posts = array(); diff --git a/templates/themes/recent/theme.php b/templates/themes/recent/theme.php index b1153477..57fe92b5 100644 --- a/templates/themes/recent/theme.php +++ b/templates/themes/recent/theme.php @@ -24,8 +24,14 @@ $this->excluded = explode(' ', $settings['exclude']); - if ($action == 'all' || $action == 'post' || $action == 'post-thread' || $action == 'post-delete') - file_write($config['dir']['home'] . $settings['html'], $this->homepage($settings)); + if ($action == 'all' || $action == 'post' || $action == 'post-thread' || $action == 'post-delete') { + if ($config['smart_build']) { + file_unlink($config['dir']['home'] . $settings['html']); + } + else { + file_write($config['dir']['home'] . $settings['html'], $this->homepage($settings)); + } + } } // Build news page diff --git a/templates/themes/ukko/theme.php b/templates/themes/ukko/theme.php index 0f7c3e1a..fb851bdd 100644 --- a/templates/themes/ukko/theme.php +++ b/templates/themes/ukko/theme.php @@ -2,6 +2,8 @@ require 'info.php'; function ukko_build($action, $settings) { + global $config; + $ukko = new ukko(); $ukko->settings = $settings; @@ -9,7 +11,13 @@ return; } - file_write($settings['uri'] . '/index.html', $ukko->build()); + if ($config['smart_build']) { + file_unlink($settings['uri'] . '/index.html'); + } + else { + file_write($settings['uri'] . '/index.html', $ukko->build()); + } + file_write($settings['uri'] . '/ukko.js', Element('themes/ukko/ukko.js', array())); } From 3b9e174173f56215f8b47e85879dcfbe1752bcc6 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 3 Apr 2015 05:57:39 +0200 Subject: [PATCH 17/70] sitemap theme for smart_build --- smart_build.php | 9 ++++++ templates/themes/sitemap/theme.php | 49 +++++++++++++++++------------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/smart_build.php b/smart_build.php index fb467233..43734e4f 100644 --- a/smart_build.php +++ b/smart_build.php @@ -105,6 +105,11 @@ function sb_recent() { return true; } +function sb_sitemap() { + rebuildTheme("sitemap", "all"); + return true; +} + $entrypoints = array(); $entrypoints['/%b/'] = 'sb_board'; @@ -130,6 +135,7 @@ $entrypoints['/*/'] = 'sb_ukko'; $entrypoints['/*/index.html'] = 'sb_ukko'; $entrypoints['/recent.html'] = 'sb_recent'; $entrypoints['/%b/catalog.html'] = 'sb_catalog'; +$entrypoints['/sitemap.xml'] = 'sb_sitemap'; $reached = false; @@ -185,6 +191,9 @@ if ($reached) { elseif (preg_match('/\.js$/', $request)) { header("Content-Type", "text/javascript; charset=utf-8"); } + elseif (preg_match('/\.xml$/', $request)) { + header("Content-Type", "application/xml"); + } else { header("Content-Type", "text/html; charset=utf-8"); } diff --git a/templates/themes/sitemap/theme.php b/templates/themes/sitemap/theme.php index 3e048dd5..368afe58 100644 --- a/templates/themes/sitemap/theme.php +++ b/templates/themes/sitemap/theme.php @@ -10,30 +10,37 @@ // - boards (board list changed) // - post (a post has been made) // - thread (a thread has been made) + + if ($action != 'all') { + if ($action != 'post-thread' && $action != 'post-delete') + return; - if ($action != 'post-thread' && $action != 'post-delete') - return; - - if (isset($settings['regen_time']) && $settings['regen_time'] > 0) { - if ($last_gen = @filemtime($settings['path'])) { - if (time() - $last_gen < (int)$settings['regen_time']) - return; // Too soon + if (isset($settings['regen_time']) && $settings['regen_time'] > 0) { + if ($last_gen = @filemtime($settings['path'])) { + if (time() - $last_gen < (int)$settings['regen_time']) + return; // Too soon + } } } - - $boards = explode(' ', $settings['boards']); - - $threads = array(); - - foreach ($boards as $board) { - $query = query(sprintf("SELECT `id` AS `thread_id`, (SELECT `time` FROM ``posts_%s`` WHERE `thread` = `thread_id` OR `id` = `thread_id` ORDER BY `time` DESC LIMIT 1) AS `lastmod` FROM ``posts_%s`` WHERE `thread` IS NULL", $board, $board)) or error(db_error()); - $threads[$board] = $query->fetchAll(PDO::FETCH_ASSOC); + + if ($config['smart_build']) { + file_unlink($settings['path']); } + else { + $boards = explode(' ', $settings['boards']); + + $threads = array(); + + foreach ($boards as $board) { + $query = query(sprintf("SELECT `id` AS `thread_id`, (SELECT `time` FROM ``posts_%s`` WHERE `thread` = `thread_id` OR `id` = `thread_id` ORDER BY `time` DESC LIMIT 1) AS `lastmod` FROM ``posts_%s`` WHERE `thread` IS NULL", $board, $board)) or error(db_error()); + $threads[$board] = $query->fetchAll(PDO::FETCH_ASSOC); + } - file_write($settings['path'], Element('themes/sitemap/sitemap.xml', Array( - 'settings' => $settings, - 'config' => $config, - 'threads' => $threads, - 'boards' => $boards, - ))); + file_write($settings['path'], Element('themes/sitemap/sitemap.xml', Array( + 'settings' => $settings, + 'config' => $config, + 'threads' => $threads, + 'boards' => $boards, + ))); + } } From da75891dd8d66070e0f7bbcb9e80fce0f4bc8126 Mon Sep 17 00:00:00 2001 From: Jayme Brereton Date: Sun, 5 Apr 2015 20:14:58 +0930 Subject: [PATCH 18/70] made the define_groups function play nice on hhvm --- inc/functions.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 0adade57..56b43f6f 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -327,8 +327,12 @@ function verbose_error_handler($errno, $errstr, $errfile, $errline) { function define_groups() { global $config; - foreach ($config['mod']['groups'] as $group_value => $group_name) - defined($group_name) or define($group_name, $group_value, true); + foreach ($config['mod']['groups'] as $group_value => $group_name) { + $group_name = strtoupper($group_name); + if(!defined($group_name)) { + define($group_name, $group_value, true); + } + } ksort($config['mod']['groups']); } From c67520d8b16964efad4451e8fd5864b4b64c6d0d Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 16:20:59 +0200 Subject: [PATCH 19/70] ukko: small optimization: move js creation to install time --- templates/themes/ukko/info.php | 1 + templates/themes/ukko/theme.php | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/themes/ukko/info.php b/templates/themes/ukko/info.php index 45a687fc..9a0a53e6 100644 --- a/templates/themes/ukko/info.php +++ b/templates/themes/ukko/info.php @@ -49,6 +49,7 @@ function ukko_install($settings) { if (!file_exists($settings['uri'])) @mkdir($settings['uri'], 0777) or error("Couldn't create " . $settings['uri'] . ". Check permissions.", true); + file_write($settings['uri'] . '/ukko.js', Element('themes/ukko/ukko.js', array())); } } diff --git a/templates/themes/ukko/theme.php b/templates/themes/ukko/theme.php index fb851bdd..ebf25c15 100644 --- a/templates/themes/ukko/theme.php +++ b/templates/themes/ukko/theme.php @@ -17,8 +17,6 @@ else { file_write($settings['uri'] . '/index.html', $ukko->build()); } - - file_write($settings['uri'] . '/ukko.js', Element('themes/ukko/ukko.js', array())); } class ukko { From 0e84743f7a62e2e18efa6fd49084b5e142a48221 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 16:38:16 +0200 Subject: [PATCH 20/70] optimization: get rid of one more sql query related to installed themes --- inc/functions.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 56b43f6f..9c3f880b 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -351,9 +351,22 @@ function rebuildThemes($action, $boardname = false) { $_board = $board; // List themes - $query = query("SELECT `theme` FROM ``theme_settings`` WHERE `name` IS NULL AND `value` IS NULL") or error(db_error()); + if ($themes = Cache::get("themes")) { + // OK, we already have themes loaded + } + else { + $query = query("SELECT `theme` FROM ``theme_settings`` WHERE `name` IS NULL AND `value` IS NULL") or error(db_error()); - while ($theme = $query->fetch(PDO::FETCH_ASSOC)) { + $themes = array(); + + while ($theme = $query->fetch(PDO::FETCH_ASSOC)) { + $themes[] = $theme; + } + + Cache::set("themes", $themes); + } + + foreach ($themes as $theme) { // Restore them $config = $_config; $board = $_board; @@ -407,6 +420,10 @@ function rebuildTheme($theme, $action, $board = false) { function themeSettings($theme) { + if ($settings = Cache::get("theme_settings_".$theme)) { + return $settings; + } + $query = prepare("SELECT `name`, `value` FROM ``theme_settings`` WHERE `theme` = :theme AND `name` IS NOT NULL"); $query->bindValue(':theme', $theme); $query->execute() or error(db_error($query)); @@ -416,6 +433,8 @@ function themeSettings($theme) { $settings[$s['name']] = $s['value']; } + Cache::set("theme_settings_".$theme, $settings); + return $settings; } From 099ea5bbbf84c8559271d7441cc003a10f6eb242 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 16:45:51 +0200 Subject: [PATCH 21/70] add tmp subdirectory (a filesystem used for further optimizations) --- tmp/cache/.gitkeep | 0 tmp/locks/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tmp/cache/.gitkeep create mode 100644 tmp/locks/.gitkeep diff --git a/tmp/cache/.gitkeep b/tmp/cache/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tmp/locks/.gitkeep b/tmp/locks/.gitkeep new file mode 100644 index 00000000..e69de29b From 3368ca1141c6c5a57d97978a7f3409a2f6d873a8 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 16:52:35 +0200 Subject: [PATCH 22/70] optimization: locale caching, so we don`t have to reparse instance-config every single time --- inc/functions.php | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 9c3f880b..1e6b9123 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -96,18 +96,28 @@ function loadConfig() { // Initialize locale as early as possible - $config['locale'] = 'en'; + // Those calls are expensive. Unfortunately, our cache system is not initialized at this point. + // So, we may store the locale in a tmp/ filesystem. - $configstr = file_get_contents('inc/instance-config.php'); + if (file_exists($fn = 'tmp/cache/locale_' . ( isset($board['uri']) ? $board['uri'] : '' ) ) ) { + $config['locale'] = file_get_contents($fn); + } + else { + $config['locale'] = 'en'; + + $configstr = file_get_contents('inc/instance-config.php'); if (isset($board['dir']) && file_exists($board['dir'] . '/config.php')) { - $configstr .= file_get_contents($board['dir'] . '/config.php'); + $configstr .= file_get_contents($board['dir'] . '/config.php'); } - $matches = array(); - preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches); - if ($matches && isset ($matches[2]) && $matches[2]) { - $matches = $matches[2]; - $config['locale'] = $matches[count($matches)-1]; + $matches = array(); + preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches); + if ($matches && isset ($matches[2]) && $matches[2]) { + $matches = $matches[2]; + $config['locale'] = $matches[count($matches)-1]; + } + + file_put_contents($fn, $config['locale']); } if ($config['locale'] != $current_locale) { From 372e3fc3fce2d40a7eb5b49195d326c2c7274d7a Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 16:59:04 +0200 Subject: [PATCH 23/70] uncache themes on settings change --- inc/mod/pages.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 7efabae6..97a33487 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -3333,10 +3333,14 @@ function mod_theme_configure($theme_name) { $query->bindValue(':value', $_POST[$conf['name']]); $query->execute() or error(db_error($query)); } - + $query = prepare("INSERT INTO ``theme_settings`` VALUES(:theme, NULL, NULL)"); $query->bindValue(':theme', $theme_name); $query->execute() or error(db_error($query)); + + // Clean cache + Cache::delete("themes"); + Cache::delete("theme_settings_".$theme); $result = true; $message = false; @@ -3384,11 +3388,15 @@ function mod_theme_uninstall($theme_name) { if (!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); - + $query = prepare("DELETE FROM ``theme_settings`` WHERE `theme` = :theme"); $query->bindValue(':theme', $theme_name); $query->execute() or error(db_error($query)); + // Clean cache + Cache::delete("themes"); + Cache::delete("theme_settings_".$theme); + header('Location: ?/themes', true, $config['redirect_http']); } From 093b1b605ba1ccc3d55d984acb315d8345cbb1a2 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 17:13:55 +0200 Subject: [PATCH 24/70] cache.php: fs cache --- inc/cache.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/inc/cache.php b/inc/cache.php index d1200919..d6bc9211 100644 --- a/inc/cache.php +++ b/inc/cache.php @@ -50,6 +50,17 @@ class Cache { case 'php': $data = isset(self::$cache[$key]) ? self::$cache[$key] : false; break; + case 'fs': + $key = str_replace('/', '::', $key); + $key = str_replace("\0", '', $key); + if (!file_exists('tmp/cache/'.$key)) { + $data = false; + } + else { + $data = file_get_contents('tmp/cache/'.$key); + $data = json_decode($data, true); + } + break; case 'redis': if (!self::$cache) self::init(); @@ -87,6 +98,11 @@ class Cache { case 'xcache': xcache_set($key, $value, $expires); break; + case 'fs': + $key = str_replace('/', '::', $key); + $key = str_replace("\0", '', $key); + file_put_contents('tmp/cache/'.$key, json_encode($value)); + break; case 'php': self::$cache[$key] = $value; break; @@ -113,6 +129,11 @@ class Cache { case 'xcache': xcache_unset($key); break; + case 'fs': + $key = str_replace('/', '::', $key); + $key = str_replace("\0", '', $key); + unlink('tmp/cache/'.$key); + break; case 'php': unset(self::$cache[$key]); break; @@ -134,6 +155,12 @@ class Cache { case 'php': self::$cache = array(); break; + case 'fs': + $files = glob('tmp/cache/*'); + foreach ($files as $file) { + unlink($file); + } + break; case 'redis': if (!self::$cache) self::init(); From 5c5751647dd2de6ede57ecf622df0cfa157bd6ed Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 18:46:45 +0200 Subject: [PATCH 25/70] update gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 8fecf08c..825799da 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,12 @@ Thumbs.db *.orig *~ +# tmp filesystem +/tmp/cache/* +/tmp/locks/* +!/tmp/cache/.gitkeep +!/tmp/locks/.gitkeep + #vichan custom favicon.ico /static/spoiler.png From 7c78dad6d89a39795d0ec35c2927609960f692d2 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 18:42:23 +0200 Subject: [PATCH 26/70] =?UTF-8?q?smart=5Fbuild.php:=20nginx=20=E2=80=93=20?= =?UTF-8?q?cache=20404=20replies=20for=20120s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- smart_build.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smart_build.php b/smart_build.php index 43734e4f..993d7858 100644 --- a/smart_build.php +++ b/smart_build.php @@ -169,7 +169,7 @@ function die_404() { global $config; else { header("Location: ".$config['page_404']); } - + header("X-Accel-Expires: 120"); die(); } From 8da7ec3bdedc480dbd4cf732e003cbb5795126e1 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 18:59:33 +0200 Subject: [PATCH 27/70] try_smarter: fix two bugs. 1. uncovered by the second, during a bump only the page the thread was on and first page were rebuild, despite threads rearranging their positions on the remaining pages. happening always. 2. during smart build, the page wasn`t ordered to be rebuilt --- inc/functions.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 1e6b9123..af029d43 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1049,8 +1049,9 @@ function bumpThread($id) { if (event('bump', $id)) return true; - if ($config['try_smarter']) - $build_pages[] = thread_find_page($id); + if ($config['try_smarter']) { + $build_pages = array_merge(range(1, thread_find_page($id)), $build_pages); + } $query = prepare(sprintf("UPDATE ``posts_%s`` SET `bump` = :time WHERE `id` = :id AND `thread` IS NULL", $board['uri'])); $query->bindValue(':time', time(), PDO::PARAM_INT); @@ -2111,6 +2112,9 @@ function buildThread($id, $return = false, $mod = false) { cache::delete("thread_{$board['uri']}_{$id}"); } + if ($config['try_smarter'] && !$mod) + $build_pages[] = thread_find_page($id); + if (!$config['smart_build'] || $return || $mod) { $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); @@ -2145,9 +2149,6 @@ function buildThread($id, $return = false, $mod = false) { 'return' => ($mod ? '?' . $board['url'] . $config['file_index'] : $config['root'] . $board['dir'] . $config['file_index']) )); - if ($config['try_smarter'] && !$mod) - $build_pages[] = thread_find_page($id); - // json api if ($config['api']['enabled']) { $api = new Api(); From c1045de87ff7bc8b1d8e1f4e0e7cb7c177db5cf3 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 19:28:00 +0200 Subject: [PATCH 28/70] infinity/smart_build: disable slugify for now --- smart_build.php | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smart_build.php b/smart_build.php index 993d7858..da1966d0 100644 --- a/smart_build.php +++ b/smart_build.php @@ -48,18 +48,6 @@ function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = return false; } - if ($slugcheck && $config['slugify']) { - global $request; - - $link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b)); - $link = "/".$b."/".$config['dir']['res'].$link; - - if ($link != $request) { - header("Location: $link", true, 301); - die(); - } - } - if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway global $request; $r = str_replace("+50", "", $request); @@ -73,9 +61,6 @@ function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = return true; } -function sb_thread_slugcheck($b, $thread) { - return sb_thread($b, $thread, true); -} function sb_thread_slugcheck50($b, $thread) { return sb_thread($b, $thread, 50); } @@ -121,12 +106,8 @@ if ($config['api']['enabled']) { $entrypoints['/%b/catalog.json'] = 'sb_api'; } -$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck'; +$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread'; $entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50'; -if ($config['slugify']) { - $entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck'; - $entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50'; -} if ($config['api']['enabled']) { $entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread'; } From 9907ae29b818624ec3c9e8a4c6c82d927270fa29 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 27 Feb 2015 21:16:03 +0100 Subject: [PATCH 29/70] rearrange config processing a bit --- inc/functions.php | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index af029d43..72487d23 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -138,10 +138,6 @@ function loadConfig() { init_locale($config['locale'], $error); } - if (!isset($__version)) - $__version = file_exists('.installed') ? trim(file_get_contents('.installed')) : false; - $config['version'] = $__version; - date_default_timezone_set($config['timezone']); if (!isset($config['global_message'])) @@ -150,6 +146,7 @@ function loadConfig() { if (!isset($config['post_url'])) $config['post_url'] = $config['root'] . $config['file_post']; + if (!isset($config['referer_match'])) if (isset($_SERVER['HTTP_HOST'])) { $config['referer_match'] = '/^' . @@ -220,19 +217,12 @@ function loadConfig() { if (!isset($config['user_flags'])) $config['user_flags'] = array(); + // Effectful config processing below: + if ($config['root_file']) { chdir($config['root_file']); } - if ($config['verbose_errors']) { - set_error_handler('verbose_error_handler'); - error_reporting(E_ALL); - ini_set('display_errors', true); - ini_set('html_errors', false); - } else { - ini_set('display_errors', false); - } - // Keep the original address to properly comply with other board configurations if (!isset($__ip)) $__ip = $_SERVER['REMOTE_ADDR']; @@ -241,11 +231,25 @@ function loadConfig() { if (preg_match('/^\:\:(ffff\:)?(\d+\.\d+\.\d+\.\d+)$/', $__ip, $m)) $_SERVER['REMOTE_ADDR'] = $m[2]; + if (!isset($__version)) + $__version = file_exists('.installed') ? trim(file_get_contents('.installed')) : false; + $config['version'] = $__version; + + if ($config['verbose_errors']) { + set_error_handler('verbose_error_handler'); + error_reporting(E_ALL); + ini_set('display_errors', true); + ini_set('html_errors', false); + } else { + ini_set('display_errors', false); + } + if ($config['syslog']) openlog('tinyboard', LOG_ODELAY, LOG_SYSLOG); // open a connection to sysem logger if ($config['recaptcha']) require_once 'inc/lib/recaptcha/recaptchalib.php'; + if ($config['cache']['enabled']) require_once 'inc/cache.php'; From 18e5de9a1ffda546313462cb22fcf94bccfa375d Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 5 Apr 2015 18:48:53 +0200 Subject: [PATCH 30/70] cache_config preliminary release --- inc/config.php | 5 ++++ inc/functions.php | 73 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/inc/config.php b/inc/config.php index 6244479f..e1ad3eec 100644 --- a/inc/config.php +++ b/inc/config.php @@ -132,6 +132,11 @@ // Tinyboard to use. $config['cache']['redis'] = array('localhost', 6379, '', 1); + // EXPERIMENTAL: Should we cache configs? Warning: this changes board behaviour, i'd say, a lot. + // If you have any lambdas/includes present in your config, you should move them to instance-functions.php + // (this file will be explicitly loaded during cache hit, but not during cache miss). + $config['cache_config'] = false; + /* * ==================== * Cookie settings diff --git a/inc/functions.php b/inc/functions.php index 72487d23..8aa0e4d8 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -50,15 +50,40 @@ $current_locale = 'en'; function loadConfig() { - global $board, $config, $__ip, $debug, $__version, $microtime_start, $current_locale; + global $board, $config, $__ip, $debug, $__version, $microtime_start, $current_locale, $events; $error = function_exists('error') ? 'error' : 'basic_error_function_because_the_other_isnt_loaded_yet'; - reset_events(); + $boardsuffix = isset($board['uri']) ? $board['uri'] : ''; if (!isset($_SERVER['REMOTE_ADDR'])) $_SERVER['REMOTE_ADDR'] = '0.0.0.0'; + if (file_exists('tmp/cache/cache_config.php')) { + require_once('tmp/cache/cache_config.php'); + } + + + if (isset($config['cache_config']) && + $config['cache_config'] && + $config = Cache::get('config_' . $boardsuffix ) ) { + $events = Cache::get('events_' . $boardsuffix ); + + if (file_exists('inc/instance-functions.php')) { + require_once('inc/instance-functions.php'); + } + + if ($config['locale'] != $current_locale) { + $current_locale = $config['locale']; + init_locale($config['locale'], $error); + } + } + else { + $config = array(); + // We will indent that later. + + reset_events(); + $arrays = array( 'db', 'api', @@ -86,7 +111,6 @@ function loadConfig() { 'dashboard_links' ); - $config = array(); foreach ($arrays as $key) { $config[$key] = array(); } @@ -99,7 +123,7 @@ function loadConfig() { // Those calls are expensive. Unfortunately, our cache system is not initialized at this point. // So, we may store the locale in a tmp/ filesystem. - if (file_exists($fn = 'tmp/cache/locale_' . ( isset($board['uri']) ? $board['uri'] : '' ) ) ) { + if (file_exists($fn = 'tmp/cache/locale_' . $boardsuffix ) ) { $config['locale'] = file_get_contents($fn); } else { @@ -138,8 +162,6 @@ function loadConfig() { init_locale($config['locale'], $error); } - date_default_timezone_set($config['timezone']); - if (!isset($config['global_message'])) $config['global_message'] = false; @@ -217,8 +239,22 @@ function loadConfig() { if (!isset($config['user_flags'])) $config['user_flags'] = array(); + if (!isset($__version)) + $__version = file_exists('.installed') ? trim(file_get_contents('.installed')) : false; + $config['version'] = $__version; + + if ($config['allow_roll']) + event_handler('post', 'diceRoller'); + + if (is_array($config['anonymous'])) + $config['anonymous'] = $config['anonymous'][array_rand($config['anonymous'])]; + + + } // Effectful config processing below: + date_default_timezone_set($config['timezone']); + if ($config['root_file']) { chdir($config['root_file']); } @@ -231,10 +267,6 @@ function loadConfig() { if (preg_match('/^\:\:(ffff\:)?(\d+\.\d+\.\d+\.\d+)$/', $__ip, $m)) $_SERVER['REMOTE_ADDR'] = $m[2]; - if (!isset($__version)) - $__version = file_exists('.installed') ? trim(file_get_contents('.installed')) : false; - $config['version'] = $__version; - if ($config['verbose_errors']) { set_error_handler('verbose_error_handler'); error_reporting(E_ALL); @@ -258,13 +290,22 @@ function loadConfig() { event_handler('post', 'postHandler'); } - if (is_array($config['anonymous'])) - $config['anonymous'] = $config['anonymous'][array_rand($config['anonymous'])]; - - if ($config['allow_roll']) - event_handler('post', 'diceRoller'); - event('load-config'); + + if ($config['cache_config'] && !isset ($config['cache_config_loaded'])) { + file_put_contents('tmp/cache/cache_config.php', ' Date: Sun, 5 Apr 2015 20:04:27 +0200 Subject: [PATCH 31/70] cache_config: fix debug notice --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 8aa0e4d8..33a6f5d4 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -295,9 +295,9 @@ function loadConfig() { if ($config['cache_config'] && !isset ($config['cache_config_loaded'])) { file_put_contents('tmp/cache/cache_config.php', ' Date: Sun, 5 Apr 2015 20:25:57 +0200 Subject: [PATCH 32/70] groups were not defined --- inc/functions.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inc/functions.php b/inc/functions.php index 33a6f5d4..30de9d15 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -69,6 +69,8 @@ function loadConfig() { $config = Cache::get('config_' . $boardsuffix ) ) { $events = Cache::get('events_' . $boardsuffix ); + define_groups(); + if (file_exists('inc/instance-functions.php')) { require_once('inc/instance-functions.php'); } From 9ca7dae9c9a553aa85e73e054b00a076a3607105 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 19:50:50 +0200 Subject: [PATCH 33/70] infinity/smart-build: clean cache on rebuilding the boards --- inc/8chan-mod-pages.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 3eb7de07..135a1c46 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -661,6 +661,18 @@ EOT; // Faster than openBoard and bypasses cache...we're trusting the PHP output // to be safe enough to run with every request, we can eval it here. eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); + // czaks: maybe reconsider using it, now that config is cached? + + // Clean the cache + if ($config['cache']['enabled']) { + cache::delete('board_' . $board['uri']); + cache::delete('all_boards'); + + cache::delete('config_' . $board['uri']); + cache::delete('events_' . $board['uri']); + + unlink('tmp/cache/locale_' . $board['uri']); + } // be smarter about rebuilds...only some changes really require us to rebuild all threads if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] @@ -686,10 +698,5 @@ EOT; $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); - if ($config['cache']['enabled']) { - cache::delete('board_' . $board['uri']); - cache::delete('all_boards'); - } - mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); }; From be4bf70278ba69a957aa953864484935932ee70b Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 20:00:18 +0200 Subject: [PATCH 34/70] infinity/smart-build: don`t include gettext php lib if a shared object is loaded: adaptation of vichan-devel/cbbebc --- inc/functions.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 30de9d15..821cc48a 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -19,7 +19,9 @@ require_once 'inc/database.php'; require_once 'inc/events.php'; require_once 'inc/api.php'; require_once 'inc/bans.php'; -require_once 'inc/lib/gettext/gettext.inc'; +if (!extension_loaded('gettext')) { + require_once 'inc/lib/gettext/gettext.inc'; +} require_once 'inc/lib/parsedown/Parsedown.php'; // todo: option for parsedown instead of Tinyboard/STI markup require_once 'inc/mod/auth.php'; From 798121060caf7d31b3e70ea295ad5759c3e0d6a1 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 22:18:05 +0200 Subject: [PATCH 35/70] infinity/smart-build: missing slugify patchset fix --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 821cc48a..480df77f 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2212,10 +2212,10 @@ function buildThread($id, $return = false, $mod = false) { } if ($config['smart_build'] && !$return && !$mod) { - $noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true); + $noko50fn = $board['dir'] . $config['dir']['res'] . sprintf($config['file_page50'], $id); file_unlink($noko50fn); - file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id))); + file_unlink($board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $id)); } else if ($return) { return $body; } else { From ca92457e889bd5dc11a34a8f3cddcc4aa48a3a0b Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 22:37:47 +0200 Subject: [PATCH 36/70] infinity/smart-build: rearrange the config files for cache_config --- inc/8chan-functions.php | 20 + inc/8chan-mod-pages-functions.php | 680 ++++++++++++++++++++++++++++++ inc/8chan-mod-pages.php | 662 +---------------------------- inc/instance-config.php | 17 +- inc/instance-functions.php | 17 + 5 files changed, 727 insertions(+), 669 deletions(-) create mode 100644 inc/8chan-mod-pages-functions.php create mode 100644 inc/instance-functions.php diff --git a/inc/8chan-functions.php b/inc/8chan-functions.php index 5a50ec74..ef289616 100644 --- a/inc/8chan-functions.php +++ b/inc/8chan-functions.php @@ -54,3 +54,23 @@ function human_time_diff( $from, $to = '' ) { return $since; } + +function is_billion_laughs($arr1, $arr2) { + $arr = array(); + foreach ($arr1 as $k => $v) { + $arr[$v] = $arr2[$k]; + } + + for ($i = 0; $i <= sizeof($arr); $i++) { + $cur = array_slice($arr, $i, 1); + $pst = array_slice($arr, 0, $i); + if (!$cur) continue; + $kk = array_keys($cur)[0]; + $vv = array_values($cur)[0]; + foreach ($pst as $k => $v) { + if (str_replace($kk, $vv, $v) != $v) + return true; + } + } + return false; +} diff --git a/inc/8chan-mod-pages-functions.php b/inc/8chan-mod-pages-functions.php new file mode 100644 index 00000000..eef96790 --- /dev/null +++ b/inc/8chan-mod-pages-functions.php @@ -0,0 +1,680 @@ + 5) + error(_('Too many tags.')); + + $delete = prepare('DELETE FROM ``board_tags`` WHERE uri = :uri'); + $delete->bindValue(':uri', $b); + $delete->execute(); + + foreach ($_POST['tags'] as $i => $tag) { + if ($tag) { + if (strlen($tag) > 255) + continue; + + $insert = prepare('INSERT INTO ``board_tags``(uri, tag) VALUES (:uri, :tag)'); + $insert->bindValue(':uri', $b); + $insert->bindValue(':tag', utf8tohtml($tag)); + $insert->execute(); + } + } + + $update = prepare('UPDATE ``boards`` SET sfw = :sfw WHERE uri = :uri'); + $update->bindValue(':uri', $b); + $update->bindValue(':sfw', isset($_POST['sfw'])); + $update->execute(); + } + $query = prepare('SELECT * FROM ``board_tags`` WHERE uri = :uri'); + $query->bindValue(':uri', $b); + $query->execute(); + + $tags = $query->fetchAll(); + + $query = prepare('SELECT `sfw` FROM ``boards`` WHERE uri = :uri'); + $query->bindValue(':uri', $b); + $query->execute(); + + $sfw = $query->fetchColumn(); + + mod_page(_('Edit tags'), 'mod/tags.html', array('board'=>$board,'token'=>make_secure_link_token('tags/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); + } + + function mod_8_reassign($b) { + global $board, $config; + + if (!openBoard($b)) + error("Could not open board!"); + + if (!hasPermission($config['mod']['reassign_board'], $b)) + error($config['error']['noaccess']); + + $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); + $mods = $query->fetchAll(); + + if (!$mods) { + error('No mods?'); + } + + $password = base64_encode(openssl_random_pseudo_bytes(9)); + $salt = generate_salt(); + $hashed = hash('sha256', $salt . sha1($password)); + + $query = prepare('UPDATE ``mods`` SET `password` = :hashed, `salt` = :salt WHERE BINARY username = :mod'); + $query->bindValue(':hashed', $hashed); + $query->bindValue(':salt', $salt); + $query->bindValue(':mod', $mods[0]['username']); + $query->execute(); + + $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at https://8ch.net/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan!"; + + modLog("Reassigned board /$b/"); + + mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); + } + + function mod_8_volunteers($b) { + global $board, $config, $pdo; + if (!hasPermission($config['mod']['edit_volunteers'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + if (isset($_POST['username'], $_POST['password'])) { + $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $count = $query->rowCount(); + $query = prepare('SELECT `username` FROM ``mods``'); + $query->execute() or error(db_error($query)); + $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); + + if ($_POST['username'] == '') + error(sprintf($config['error']['required'], 'username')); + if ($_POST['password'] == '') + error(sprintf($config['error']['required'], 'password')); + if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) + error(_('Invalid username')); + + if ($count > 10) { + error(_('Too many board volunteers!')); + } + + foreach ($volunteers as $i => $v) { + if (strtolower($_POST['username']) == strtolower($v['username'])) { + error(_('Refusing to create a volunteer with the same username as an existing one.')); + } + } + + $salt = generate_salt(); + $password = hash('sha256', $salt . sha1($_POST['password'])); + + $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, 19, :board)'); + $query->bindValue(':username', $_POST['username']); + $query->bindValue(':password', $password); + $query->bindValue(':salt', $salt); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + + $userID = $pdo->lastInsertId(); + + + modLog('Created a new volunteer: ' . utf8tohtml($_POST['username']) . ' (#' . $userID . ')'); + } + + if (isset($_POST['delete'])){ + foreach ($_POST['delete'] as $i => $d){ + $query = prepare('SELECT * FROM ``mods`` WHERE id = :id'); + $query->bindValue(':id', $d); + $query->execute() or error(db_error($query)); + + $result = $query->fetch(PDO::FETCH_ASSOC); + + if (!$result) { + error(_('Volunteer does not exist!')); + } + + if ($result['boards'] != $b || $result['type'] != BOARDVOLUNTEER) { + error($config['error']['noaccess']); + } + + $query = prepare('DELETE FROM ``mods`` WHERE id = :id'); + $query->bindValue(':id', $d); + $query->execute() or error(db_error($query)); + } + } + + $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); + + mod_page(_('Edit volunteers'), 'mod/volunteers.html', array('board'=>$board,'token'=>make_secure_link_token('volunteers/'.$board['uri']),'volunteers'=>$volunteers)); + + } + + function mod_8_flags($b) { + global $config, $mod, $board; + require_once 'inc/image.php'; + if (!hasPermission($config['mod']['edit_flags'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + if (file_exists("$b/flags.ser")) + $config['user_flags'] = unserialize(file_get_contents("$b/flags.ser")); + + $dir = 'static/custom-flags/'.$b; + + if (!is_dir($dir)){ + mkdir($dir, 0777, true); + } + + function handle_file($id = false, $description, $b, $dir) { + global $config; + + if (!isset($description) and $description) + error(_('You must enter a flag description!')); + + if (strlen($description) > 255) + error(_('Flag description too long!')); + + if ($id) { + $f = 'flag-'.$id; + } else { + $f = 'file'; + $id = time() . substr(microtime(), 2, 3); + } + + $upload = $_FILES[$f]['tmp_name']; + $banners = array_diff(scandir($dir), array('..', '.')); + + if (!is_readable($upload)) + error($config['error']['nomove']); + + $extension = strtolower(mb_substr($_FILES[$f]['name'], mb_strrpos($_FILES[$f]['name'], '.') + 1)); + + if ($extension != 'png') { + error(_('Flags must be in PNG format.')); + } + + if (filesize($upload) > 48000){ + error(_('File too large!')); + } + + if (!$size = @getimagesize($upload)) { + error($config['error']['invalidimg']); + } + + if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ + error(_('Image wrong size!')); + } + if (sizeof($banners) > 256) { + error(_('Too many flags.')); + } + + copy($upload, "$dir/$id.$extension"); + purge("$dir/$id.$extension"); + $config['user_flags'][$id] = utf8tohtml($description); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + // Handle a new flag, if any. + if (isset($_FILES['file'])){ + handle_file(false, $_POST['description'], $b, $dir); + } + + // Handle edits to existing flags. + foreach ($_FILES as $k => $a) { + if (empty($_FILES[$k]['tmp_name'])) continue; + + if (preg_match('/^flag-(\d+)$/', $k, $matches)) { + $id = (int)$matches[1]; + if (!isset($_POST['description-'.$id])) continue; + + if (isset($config['user_flags'][$id])) { + handle_file($id, $_POST['description-'.$id], $b, $dir); + } + } + } + + // Description just changed, flag not edited. + foreach ($_POST as $k => $v) { + if (!preg_match('/^description-(\d+)$/', $k, $matches)) continue; + $id = (int)$matches[1]; + if (!isset($_POST['description-'.$id])) continue; + + $description = $_POST['description-'.$id]; + + if (strlen($description) > 255) + error(_('Flag description too long!')); + $config['user_flags'][$id] = utf8tohtml($description); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $flags = << $d){ + if (!preg_match('/[0-9+]/', $d)){ + error('Nice try.'); + } + unlink("$dir/$d.png"); + $id = explode('.', $d)[0]; + unset($config['user_flags'][$id]); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + } + + if (isset($_POST['alphabetize'])) { + asort($config['user_flags'], SORT_NATURAL | SORT_FLAG_CASE); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + $banners = array_diff(scandir($dir), array('..', '.')); + mod_page(_('Edit flags'), 'mod/flags.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); + } + + function mod_8_banners($b) { + global $config, $mod, $board; + require_once 'inc/image.php'; + + if (!hasPermission($config['mod']['edit_banners'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + $dir = 'static/banners/'.$b; + + if (!is_dir($dir)){ + mkdir($dir, 0777, true); + } + + + if (isset($_FILES['file'])){ + $upload = $_FILES['file']['tmp_name']; + $banners = array_diff(scandir($dir), array('..', '.')); + + if (!is_readable($upload)) + error($config['error']['nomove']); + + $id = time() . substr(microtime(), 2, 3); + $extension = strtolower(mb_substr($_FILES['file']['name'], mb_strrpos($_FILES['file']['name'], '.') + 1)); + + if (!in_array($extension, array('jpg','jpeg','png','gif'))){ + error('Not an image extension.'); + } + + if (filesize($upload) > 512000){ + error('File too large!'); + } + + if (!$size = @getimagesize($upload)) { + error($config['error']['invalidimg']); + } + + if ($size[0] != 300 or $size[1] != 100){ + error('Image wrong size!'); + } + if (sizeof($banners) >= 50) { + error('Too many banners.'); + } + + copy($upload, "$dir/$id.$extension"); + } + + if (isset($_POST['delete'])){ + foreach ($_POST['delete'] as $i => $d){ + if (!preg_match('/[0-9+]\.(png|jpeg|jpg|gif)/', $d)){ + error('Nice try.'); + } + unlink("$dir/$d"); + } + } + + $banners = array_diff(scandir($dir), array('..', '.')); + mod_page(_('Edit banners'), 'mod/banners.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); + + } + + function mod_8_settings($b) { + global $config, $mod; + + //if ($b === 'infinity' && $mod['type'] !== ADMIN) + // error('Settings temporarily disabled for this board.'); + + if (!in_array($b, $mod['boards']) and $mod['boards'][0] != '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_settings'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + $possible_languages = array_diff(scandir('inc/locale/'), array('..', '.', '.tx', 'README.md')); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $title = $_POST['title']; + $subtitle = $_POST['subtitle']; + $country_flags = isset($_POST['country_flags']) ? 'true' : 'false'; + $field_disable_name = isset($_POST['field_disable_name']) ? 'true' : 'false'; + $enable_embedding = isset($_POST['enable_embedding']) ? 'true' : 'false'; + $force_image_op = isset($_POST['force_image_op']) ? 'true' : 'false'; + $disable_images = isset($_POST['disable_images']) ? 'true' : 'false'; + $poster_ids = isset($_POST['poster_ids']) ? 'true' : 'false'; + $show_sages = isset($_POST['show_sages']) ? 'true' : 'false'; + $auto_unicode = isset($_POST['auto_unicode']) ? 'true' : 'false'; + $strip_combining_chars = isset($_POST['strip_combining_chars']) ? 'true' : 'false'; + $allow_roll = isset($_POST['allow_roll']) ? 'true' : 'false'; + $image_reject_repost = isset($_POST['image_reject_repost']) ? 'true' : 'false'; + $image_reject_repost_in_thread = isset($_POST['image_reject_repost_in_thread']) ? 'true' : 'false'; + $early_404 = isset($_POST['early_404']) ? 'true' : 'false'; + $allow_delete = isset($_POST['allow_delete']) ? 'true' : 'false'; + $allow_flash = isset($_POST['allow_flash']) ? '$config[\'allowed_ext_files\'][] = \'swf\';' : ''; + $allow_pdf = isset($_POST['allow_pdf']) ? '$config[\'allowed_ext_files\'][] = \'pdf\';' : ''; + $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; + $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; + $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; + $captcha = isset($_POST['captcha']) ? 'true' : 'false'; + $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; + $force_flag = isset($_POST['force_flag']) ? 'true' : 'false'; + $tor_posting = isset($_POST['tor_posting']) ? 'true' : 'false'; + $new_thread_capt = isset($_POST['new_thread_capt']) ? 'true' : 'false'; + $oekaki = isset($_POST['oekaki']) ? 'true' : 'false'; + + if ($_POST['locale'] !== 'en' && in_array($_POST['locale'], $possible_languages)) { + $locale = "\$config['locale'] = '{$_POST['locale']}.UTF-8';"; + } else { + $locale = ''; + } + + if (isset($_POST['max_images']) && (int)$_POST['max_images'] && (int)$_POST['max_images'] <= 5) { + $_POST['max_images'] = (int)$_POST['max_images']; + $multiimage = "\$config['max_images'] = {$_POST['max_images']}; + \$config['additional_javascript'][] = 'js/multi-image.js';"; + } else { + $multiimage = ''; + } + + $anonymous = base64_encode($_POST['anonymous']); + $blotter = base64_encode(purify_html(html_entity_decode($_POST['blotter']))); + $add_to_config = @file_get_contents($b.'/extra_config.php'); + $replace = ''; + + if (isset($_POST['replace'])) { + if (sizeof($_POST['replace']) > 200 || sizeof($_POST['with']) > 200) { + error(_('Sorry, max 200 wordfilters allowed.')); + } + if (count($_POST['replace']) == count($_POST['with'])) { + foreach ($_POST['replace'] as $i => $r ) { + if ($r !== '') { + $w = $_POST['with'][$i]; + + if (strlen($w) > 255) { + error(sprintf(_('Sorry, %s is too long. Max replacement is 255 characters'), utf8tohtml($w))); + } + + $replace .= '$config[\'wordfilters\'][] = array(base64_decode(\'' . base64_encode($r) . '\'), base64_decode(\'' . base64_encode($w) . '\'));'; + } + } + } + if (is_billion_laughs($_POST['replace'], $_POST['with'])) { + error(_('Wordfilters may not wordfilter previous wordfilters. For example, if a filters to bb and b filters to cc, that is not allowed.')); + } + } + + if (isset($_POST['hour_max_threads']) && in_array($_POST['hour_max_threads'], ['10', '25', '50', '100'])) { + $hour_max_threads = $_POST['hour_max_threads']; + } else { + $hour_max_threads = 'false'; + } + + if (isset($_POST['max_pages'])) { + $mp = (int)$_POST['max_pages']; + if ($mp > 25 || $mp < 2) { + $max_pages = 15; + } else { + $max_pages = $mp; + } + } else { + $max_pages = 15; + } + + if (isset($_POST['reply_limit'])) { + $rl = (int)$_POST['reply_limit']; + if ($rl > 750 || $rl < 250 || $rl % 25) { + $reply_limit = 250; + } else { + $reply_limit = $rl; + } + } else { + $reply_limit = 250; + } + + if (isset($_POST['max_newlines'])) { + $mn = (int)$_POST['max_newlines']; + if ($mn < 20 || $mn > 300) { + $max_newlines = 0; + } else { + $max_newlines = $mn; + } + } else { + $max_newlines = $mn; + } + + if (!(strlen($title) < 40)) + error('Invalid title'); + if (!(strlen($subtitle) < 200)) + error('Invalid subtitle'); + + $query = prepare('UPDATE ``boards`` SET `title` = :title, `subtitle` = :subtitle, `indexed` = :indexed, `public_bans` = :public_bans, `public_logs` = :public_logs, `8archive` = :8archive WHERE `uri` = :uri'); + $query->bindValue(':title', $title); + $query->bindValue(':subtitle', $subtitle); + $query->bindValue(':uri', $b); + $query->bindValue(':indexed', !isset($_POST['meta_noindex'])); + $query->bindValue(':public_bans', isset($_POST['public_bans'])); + $query->bindValue(':public_logs', (int)$_POST['public_logs']); + $query->bindValue(':8archive', isset($_POST['8archive'])); + $query->execute() or error(db_error($query)); + + $config_file = <<fetchAll(PDO::FETCH_ASSOC))); + file_write($b.'/config.php', $config_file); + file_write('stylesheets/board/'.$b.'.css', $clean_css); + + $_config = $config; + unset($config['wordfilters']); + + // Faster than openBoard and bypasses cache...we're trusting the PHP output + // to be safe enough to run with every request, we can eval it here. + eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); + // czaks: maybe reconsider using it, now that config is cached? + + // Clean the cache + if ($config['cache']['enabled']) { + cache::delete('board_' . $board['uri']); + cache::delete('all_boards'); + + cache::delete('config_' . $board['uri']); + cache::delete('events_' . $board['uri']); + + unlink('tmp/cache/locale_' . $board['uri']); + } + + // be smarter about rebuilds...only some changes really require us to rebuild all threads + if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] + || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ + || $_config['captcha']['extra'] != $config['captcha']['extra'] + || $_config['blotter'] != $config['blotter'] + || $_config['field_disable_name'] != $config['field_disable_name'] + || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { + buildIndex(); + $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + buildThread($post['id']); + } + } + + modLog('Edited board settings', $b); + } + + $query = prepare('SELECT * FROM boards WHERE uri = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $board = $query->fetchAll()[0]; + + $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); + + mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); + } diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 135a1c46..7894be58 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -1,26 +1,4 @@ $v) { - $arr[$v] = $arr2[$k]; - } - - for ($i = 0; $i <= sizeof($arr); $i++) { - $cur = array_slice($arr, $i, 1); - $pst = array_slice($arr, 0, $i); - if (!$cur) continue; - $kk = array_keys($cur)[0]; - $vv = array_values($cur)[0]; - foreach ($pst as $k => $v) { - if (str_replace($kk, $vv, $v) != $v) - return true; - } - } - return false; - } - } - $config['mod']['show_ip'] = GLOBALVOLUNTEER; $config['mod']['show_ip_less'] = BOARDVOLUNTEER; $config['mod']['manageusers'] = GLOBALVOLUNTEER; @@ -67,636 +45,10 @@ $config['mod']['shadow_capcode'] = 'Global Volunteer'; - $config['mod']['custom_pages']['/tags/(\%b)'] = function ($b) { - global $board, $config; - - if (!openBoard($b)) - error("Could not open board!"); - - if (!hasPermission($config['mod']['edit_tags'], $b)) - error($config['error']['noaccess']); - - if (isset($_POST['tags'])) { - if (sizeof($_POST['tags']) > 5) - error(_('Too many tags.')); - - $delete = prepare('DELETE FROM ``board_tags`` WHERE uri = :uri'); - $delete->bindValue(':uri', $b); - $delete->execute(); - - foreach ($_POST['tags'] as $i => $tag) { - if ($tag) { - if (strlen($tag) > 255) - continue; - - $insert = prepare('INSERT INTO ``board_tags``(uri, tag) VALUES (:uri, :tag)'); - $insert->bindValue(':uri', $b); - $insert->bindValue(':tag', utf8tohtml($tag)); - $insert->execute(); - } - } - - $update = prepare('UPDATE ``boards`` SET sfw = :sfw WHERE uri = :uri'); - $update->bindValue(':uri', $b); - $update->bindValue(':sfw', isset($_POST['sfw'])); - $update->execute(); - } - $query = prepare('SELECT * FROM ``board_tags`` WHERE uri = :uri'); - $query->bindValue(':uri', $b); - $query->execute(); - - $tags = $query->fetchAll(); - - $query = prepare('SELECT `sfw` FROM ``boards`` WHERE uri = :uri'); - $query->bindValue(':uri', $b); - $query->execute(); - - $sfw = $query->fetchColumn(); - - mod_page(_('Edit tags'), 'mod/tags.html', array('board'=>$board,'token'=>make_secure_link_token('tags/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); - }; - - $config['mod']['custom_pages']['/reassign/(\%b)'] = function($b) { - global $board, $config; - - if (!openBoard($b)) - error("Could not open board!"); - - if (!hasPermission($config['mod']['reassign_board'], $b)) - error($config['error']['noaccess']); - - $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); - $mods = $query->fetchAll(); - - if (!$mods) { - error('No mods?'); - } - - $password = base64_encode(openssl_random_pseudo_bytes(9)); - $salt = generate_salt(); - $hashed = hash('sha256', $salt . sha1($password)); - - $query = prepare('UPDATE ``mods`` SET `password` = :hashed, `salt` = :salt WHERE BINARY username = :mod'); - $query->bindValue(':hashed', $hashed); - $query->bindValue(':salt', $salt); - $query->bindValue(':mod', $mods[0]['username']); - $query->execute(); - - $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at https://8ch.net/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan!"; - - modLog("Reassigned board /$b/"); - - mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); - }; - - $config['mod']['custom_pages']['/volunteers/(\%b)'] = function($b) { - global $board, $config, $pdo; - if (!hasPermission($config['mod']['edit_volunteers'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - if (isset($_POST['username'], $_POST['password'])) { - $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $count = $query->rowCount(); - $query = prepare('SELECT `username` FROM ``mods``'); - $query->execute() or error(db_error($query)); - $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); - - if ($_POST['username'] == '') - error(sprintf($config['error']['required'], 'username')); - if ($_POST['password'] == '') - error(sprintf($config['error']['required'], 'password')); - if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) - error(_('Invalid username')); - - if ($count > 10) { - error(_('Too many board volunteers!')); - } - - foreach ($volunteers as $i => $v) { - if (strtolower($_POST['username']) == strtolower($v['username'])) { - error(_('Refusing to create a volunteer with the same username as an existing one.')); - } - } - - $salt = generate_salt(); - $password = hash('sha256', $salt . sha1($_POST['password'])); - - $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, 19, :board)'); - $query->bindValue(':username', $_POST['username']); - $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - - $userID = $pdo->lastInsertId(); - - - modLog('Created a new volunteer: ' . utf8tohtml($_POST['username']) . ' (#' . $userID . ')'); - } - - if (isset($_POST['delete'])){ - foreach ($_POST['delete'] as $i => $d){ - $query = prepare('SELECT * FROM ``mods`` WHERE id = :id'); - $query->bindValue(':id', $d); - $query->execute() or error(db_error($query)); - - $result = $query->fetch(PDO::FETCH_ASSOC); - - if (!$result) { - error(_('Volunteer does not exist!')); - } - - if ($result['boards'] != $b || $result['type'] != BOARDVOLUNTEER) { - error($config['error']['noaccess']); - } - - $query = prepare('DELETE FROM ``mods`` WHERE id = :id'); - $query->bindValue(':id', $d); - $query->execute() or error(db_error($query)); - } - } - - $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); - - mod_page(_('Edit volunteers'), 'mod/volunteers.html', array('board'=>$board,'token'=>make_secure_link_token('volunteers/'.$board['uri']),'volunteers'=>$volunteers)); - - }; - - $config['mod']['custom_pages']['/flags/(\%b)'] = function($b) { - global $config, $mod, $board; - require_once 'inc/image.php'; - if (!hasPermission($config['mod']['edit_flags'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - if (file_exists("$b/flags.ser")) - $config['user_flags'] = unserialize(file_get_contents("$b/flags.ser")); - - $dir = 'static/custom-flags/'.$b; - - if (!is_dir($dir)){ - mkdir($dir, 0777, true); - } - - function handle_file($id = false, $description, $b, $dir) { - global $config; - - if (!isset($description) and $description) - error(_('You must enter a flag description!')); - - if (strlen($description) > 255) - error(_('Flag description too long!')); - - if ($id) { - $f = 'flag-'.$id; - } else { - $f = 'file'; - $id = time() . substr(microtime(), 2, 3); - } - - $upload = $_FILES[$f]['tmp_name']; - $banners = array_diff(scandir($dir), array('..', '.')); - - if (!is_readable($upload)) - error($config['error']['nomove']); - - $extension = strtolower(mb_substr($_FILES[$f]['name'], mb_strrpos($_FILES[$f]['name'], '.') + 1)); - - if ($extension != 'png') { - error(_('Flags must be in PNG format.')); - } - - if (filesize($upload) > 48000){ - error(_('File too large!')); - } - - if (!$size = @getimagesize($upload)) { - error($config['error']['invalidimg']); - } - - if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ - error(_('Image wrong size!')); - } - if (sizeof($banners) > 256) { - error(_('Too many flags.')); - } - - copy($upload, "$dir/$id.$extension"); - purge("$dir/$id.$extension"); - $config['user_flags'][$id] = utf8tohtml($description); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - // Handle a new flag, if any. - if (isset($_FILES['file'])){ - handle_file(false, $_POST['description'], $b, $dir); - } - - // Handle edits to existing flags. - foreach ($_FILES as $k => $a) { - if (empty($_FILES[$k]['tmp_name'])) continue; - - if (preg_match('/^flag-(\d+)$/', $k, $matches)) { - $id = (int)$matches[1]; - if (!isset($_POST['description-'.$id])) continue; - - if (isset($config['user_flags'][$id])) { - handle_file($id, $_POST['description-'.$id], $b, $dir); - } - } - } - - // Description just changed, flag not edited. - foreach ($_POST as $k => $v) { - if (!preg_match('/^description-(\d+)$/', $k, $matches)) continue; - $id = (int)$matches[1]; - if (!isset($_POST['description-'.$id])) continue; - - $description = $_POST['description-'.$id]; - - if (strlen($description) > 255) - error(_('Flag description too long!')); - $config['user_flags'][$id] = utf8tohtml($description); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $flags = << $d){ - if (!preg_match('/[0-9+]/', $d)){ - error('Nice try.'); - } - unlink("$dir/$d.png"); - $id = explode('.', $d)[0]; - unset($config['user_flags'][$id]); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - } - - if (isset($_POST['alphabetize'])) { - asort($config['user_flags'], SORT_NATURAL | SORT_FLAG_CASE); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - $banners = array_diff(scandir($dir), array('..', '.')); - mod_page(_('Edit flags'), 'mod/flags.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); - }; - - $config['mod']['custom_pages']['/banners/(\%b)'] = function($b) { - global $config, $mod, $board; - require_once 'inc/image.php'; - - if (!hasPermission($config['mod']['edit_banners'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - $dir = 'static/banners/'.$b; - - if (!is_dir($dir)){ - mkdir($dir, 0777, true); - } - - - if (isset($_FILES['file'])){ - $upload = $_FILES['file']['tmp_name']; - $banners = array_diff(scandir($dir), array('..', '.')); - - if (!is_readable($upload)) - error($config['error']['nomove']); - - $id = time() . substr(microtime(), 2, 3); - $extension = strtolower(mb_substr($_FILES['file']['name'], mb_strrpos($_FILES['file']['name'], '.') + 1)); - - if (!in_array($extension, array('jpg','jpeg','png','gif'))){ - error('Not an image extension.'); - } - - if (filesize($upload) > 512000){ - error('File too large!'); - } - - if (!$size = @getimagesize($upload)) { - error($config['error']['invalidimg']); - } - - if ($size[0] != 300 or $size[1] != 100){ - error('Image wrong size!'); - } - if (sizeof($banners) >= 50) { - error('Too many banners.'); - } - - copy($upload, "$dir/$id.$extension"); - } - - if (isset($_POST['delete'])){ - foreach ($_POST['delete'] as $i => $d){ - if (!preg_match('/[0-9+]\.(png|jpeg|jpg|gif)/', $d)){ - error('Nice try.'); - } - unlink("$dir/$d"); - } - } - - $banners = array_diff(scandir($dir), array('..', '.')); - mod_page(_('Edit banners'), 'mod/banners.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); - - }; - - $config['mod']['custom_pages']['/settings/(\%b)'] = function($b) { - global $config, $mod; - - //if ($b === 'infinity' && $mod['type'] !== ADMIN) - // error('Settings temporarily disabled for this board.'); - - if (!in_array($b, $mod['boards']) and $mod['boards'][0] != '*') - error($config['error']['noaccess']); - - if (!hasPermission($config['mod']['edit_settings'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - $possible_languages = array_diff(scandir('inc/locale/'), array('..', '.', '.tx', 'README.md')); - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $title = $_POST['title']; - $subtitle = $_POST['subtitle']; - $country_flags = isset($_POST['country_flags']) ? 'true' : 'false'; - $field_disable_name = isset($_POST['field_disable_name']) ? 'true' : 'false'; - $enable_embedding = isset($_POST['enable_embedding']) ? 'true' : 'false'; - $force_image_op = isset($_POST['force_image_op']) ? 'true' : 'false'; - $disable_images = isset($_POST['disable_images']) ? 'true' : 'false'; - $poster_ids = isset($_POST['poster_ids']) ? 'true' : 'false'; - $show_sages = isset($_POST['show_sages']) ? 'true' : 'false'; - $auto_unicode = isset($_POST['auto_unicode']) ? 'true' : 'false'; - $strip_combining_chars = isset($_POST['strip_combining_chars']) ? 'true' : 'false'; - $allow_roll = isset($_POST['allow_roll']) ? 'true' : 'false'; - $image_reject_repost = isset($_POST['image_reject_repost']) ? 'true' : 'false'; - $image_reject_repost_in_thread = isset($_POST['image_reject_repost_in_thread']) ? 'true' : 'false'; - $early_404 = isset($_POST['early_404']) ? 'true' : 'false'; - $allow_delete = isset($_POST['allow_delete']) ? 'true' : 'false'; - $allow_flash = isset($_POST['allow_flash']) ? '$config[\'allowed_ext_files\'][] = \'swf\';' : ''; - $allow_pdf = isset($_POST['allow_pdf']) ? '$config[\'allowed_ext_files\'][] = \'pdf\';' : ''; - $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; - $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; - $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; - $captcha = isset($_POST['captcha']) ? 'true' : 'false'; - $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; - $force_flag = isset($_POST['force_flag']) ? 'true' : 'false'; - $tor_posting = isset($_POST['tor_posting']) ? 'true' : 'false'; - $new_thread_capt = isset($_POST['new_thread_capt']) ? 'true' : 'false'; - $oekaki = isset($_POST['oekaki']) ? 'true' : 'false'; - - if ($_POST['locale'] !== 'en' && in_array($_POST['locale'], $possible_languages)) { - $locale = "\$config['locale'] = '{$_POST['locale']}.UTF-8';"; - } else { - $locale = ''; - } - - if (isset($_POST['max_images']) && (int)$_POST['max_images'] && (int)$_POST['max_images'] <= 5) { - $_POST['max_images'] = (int)$_POST['max_images']; - $multiimage = "\$config['max_images'] = {$_POST['max_images']}; - \$config['additional_javascript'][] = 'js/multi-image.js';"; - } else { - $multiimage = ''; - } - - $anonymous = base64_encode($_POST['anonymous']); - $blotter = base64_encode(purify_html(html_entity_decode($_POST['blotter']))); - $add_to_config = @file_get_contents($b.'/extra_config.php'); - $replace = ''; - - if (isset($_POST['replace'])) { - if (sizeof($_POST['replace']) > 200 || sizeof($_POST['with']) > 200) { - error(_('Sorry, max 200 wordfilters allowed.')); - } - if (count($_POST['replace']) == count($_POST['with'])) { - foreach ($_POST['replace'] as $i => $r ) { - if ($r !== '') { - $w = $_POST['with'][$i]; - - if (strlen($w) > 255) { - error(sprintf(_('Sorry, %s is too long. Max replacement is 255 characters'), utf8tohtml($w))); - } - - $replace .= '$config[\'wordfilters\'][] = array(base64_decode(\'' . base64_encode($r) . '\'), base64_decode(\'' . base64_encode($w) . '\'));'; - } - } - } - if (is_billion_laughs($_POST['replace'], $_POST['with'])) { - error(_('Wordfilters may not wordfilter previous wordfilters. For example, if a filters to bb and b filters to cc, that is not allowed.')); - } - } - - if (isset($_POST['hour_max_threads']) && in_array($_POST['hour_max_threads'], ['10', '25', '50', '100'])) { - $hour_max_threads = $_POST['hour_max_threads']; - } else { - $hour_max_threads = 'false'; - } - - if (isset($_POST['max_pages'])) { - $mp = (int)$_POST['max_pages']; - if ($mp > 25 || $mp < 2) { - $max_pages = 15; - } else { - $max_pages = $mp; - } - } else { - $max_pages = 15; - } - - if (isset($_POST['reply_limit'])) { - $rl = (int)$_POST['reply_limit']; - if ($rl > 750 || $rl < 250 || $rl % 25) { - $reply_limit = 250; - } else { - $reply_limit = $rl; - } - } else { - $reply_limit = 250; - } - - if (isset($_POST['max_newlines'])) { - $mn = (int)$_POST['max_newlines']; - if ($mn < 20 || $mn > 300) { - $max_newlines = 0; - } else { - $max_newlines = $mn; - } - } else { - $max_newlines = $mn; - } - - if (!(strlen($title) < 40)) - error('Invalid title'); - if (!(strlen($subtitle) < 200)) - error('Invalid subtitle'); - - $query = prepare('UPDATE ``boards`` SET `title` = :title, `subtitle` = :subtitle, `indexed` = :indexed, `public_bans` = :public_bans, `public_logs` = :public_logs, `8archive` = :8archive WHERE `uri` = :uri'); - $query->bindValue(':title', $title); - $query->bindValue(':subtitle', $subtitle); - $query->bindValue(':uri', $b); - $query->bindValue(':indexed', !isset($_POST['meta_noindex'])); - $query->bindValue(':public_bans', isset($_POST['public_bans'])); - $query->bindValue(':public_logs', (int)$_POST['public_logs']); - $query->bindValue(':8archive', isset($_POST['8archive'])); - $query->execute() or error(db_error($query)); - - $config_file = <<fetchAll(PDO::FETCH_ASSOC))); - file_write($b.'/config.php', $config_file); - file_write('stylesheets/board/'.$b.'.css', $clean_css); - - $_config = $config; - unset($config['wordfilters']); - - // Faster than openBoard and bypasses cache...we're trusting the PHP output - // to be safe enough to run with every request, we can eval it here. - eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); - // czaks: maybe reconsider using it, now that config is cached? - - // Clean the cache - if ($config['cache']['enabled']) { - cache::delete('board_' . $board['uri']); - cache::delete('all_boards'); - - cache::delete('config_' . $board['uri']); - cache::delete('events_' . $board['uri']); - - unlink('tmp/cache/locale_' . $board['uri']); - } - - // be smarter about rebuilds...only some changes really require us to rebuild all threads - if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] - || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ - || $_config['captcha']['extra'] != $config['captcha']['extra'] - || $_config['blotter'] != $config['blotter'] - || $_config['field_disable_name'] != $config['field_disable_name'] - || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { - buildIndex(); - $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); - while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - buildThread($post['id']); - } - } - - modLog('Edited board settings', $b); - } - - $query = prepare('SELECT * FROM boards WHERE uri = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $board = $query->fetchAll()[0]; - - $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); - - mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); - }; + // Mod pages code now resides in 8chan-mod-pages-functions.php file + $config['mod']['custom_pages']['/tags/(\%b)'] = '8_tags'; + $config['mod']['custom_pages']['/reassign/(\%b)'] = '8_reassign'; + $config['mod']['custom_pages']['/volunteers/(\%b)'] = '8_volunteers'; + $config['mod']['custom_pages']['/flags/(\%b)'] = '8_flags'; + $config['mod']['custom_pages']['/banners/(\%b)'] = '8_banners'; + $config['mod']['custom_pages']['/settings/(\%b)'] = '8_settings'; diff --git a/inc/instance-config.php b/inc/instance-config.php index bc8d63da..152bb9d4 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -8,7 +8,7 @@ * You can copy values from config.php (defaults) and paste them here. */ require_once "lib/htmlpurifier-4.6.0/library/HTMLPurifier.auto.php"; - require_once "8chan-functions.php"; + require_once "instance-functions.php"; // Note - you may want to change some of these in secrets.php instead of here // See the secrets.example.php file @@ -177,19 +177,7 @@ $config['hour_max_threads'] = 10; $config['filters'][] = array( 'condition' => array( - 'custom' => function($post) { - global $config, $board; - if (!$config['hour_max_threads']) return false; - - if ($post['op']) { - $query = prepare(sprintf('SELECT COUNT(*) AS `count` FROM ``posts_%s`` WHERE `thread` IS NULL AND FROM_UNIXTIME(`time`) > DATE_SUB(NOW(), INTERVAL 1 HOUR);', $board['uri'])); - $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); - $query->execute() or error(db_error($query)); - $r = $query->fetch(PDO::FETCH_ASSOC); - - return ($r['count'] > $config['hour_max_threads']); - } - } + 'custom' => 'test_posts_per_hour' ), 'action' => 'reject', 'message' => 'On this board, to prevent raids the number of threads that can be created per hour is limited. Please try again later, or post in an existing thread.' @@ -212,6 +200,7 @@ $config['enable_antibot'] = false; $config['spam']['unicode'] = false; $config['twig_cache'] = false; $config['report_captcha'] = true; + // 8chan specific mod pages require '8chan-mod-pages.php'; diff --git a/inc/instance-functions.php b/inc/instance-functions.php new file mode 100644 index 00000000..f291714f --- /dev/null +++ b/inc/instance-functions.php @@ -0,0 +1,17 @@ + DATE_SUB(NOW(), INTERVAL 1 HOUR);', $board[$ + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->execute() or error(db_error($query)); + $r = $query->fetch(PDO::FETCH_ASSOC); + + return ($r['count'] > $config['hour_max_threads']); + } +} From a123d8b2eefd71110d9bba4b66d221989db7a2bb Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 22:42:15 +0200 Subject: [PATCH 37/70] infinity/smart-build: do it other way around (esthetics) --- inc/8chan-mod-config.php | 54 +++ inc/8chan-mod-pages-functions.php | 680 ------------------------------ inc/8chan-mod-pages.php | 640 +++++++++++++++++++++++++++- inc/instance-config.php | 2 +- inc/instance-functions.php | 2 +- 5 files changed, 689 insertions(+), 689 deletions(-) create mode 100644 inc/8chan-mod-config.php delete mode 100644 inc/8chan-mod-pages-functions.php diff --git a/inc/8chan-mod-config.php b/inc/8chan-mod-config.php new file mode 100644 index 00000000..7894be58 --- /dev/null +++ b/inc/8chan-mod-config.php @@ -0,0 +1,54 @@ + 5) - error(_('Too many tags.')); - - $delete = prepare('DELETE FROM ``board_tags`` WHERE uri = :uri'); - $delete->bindValue(':uri', $b); - $delete->execute(); - - foreach ($_POST['tags'] as $i => $tag) { - if ($tag) { - if (strlen($tag) > 255) - continue; - - $insert = prepare('INSERT INTO ``board_tags``(uri, tag) VALUES (:uri, :tag)'); - $insert->bindValue(':uri', $b); - $insert->bindValue(':tag', utf8tohtml($tag)); - $insert->execute(); - } - } - - $update = prepare('UPDATE ``boards`` SET sfw = :sfw WHERE uri = :uri'); - $update->bindValue(':uri', $b); - $update->bindValue(':sfw', isset($_POST['sfw'])); - $update->execute(); - } - $query = prepare('SELECT * FROM ``board_tags`` WHERE uri = :uri'); - $query->bindValue(':uri', $b); - $query->execute(); - - $tags = $query->fetchAll(); - - $query = prepare('SELECT `sfw` FROM ``boards`` WHERE uri = :uri'); - $query->bindValue(':uri', $b); - $query->execute(); - - $sfw = $query->fetchColumn(); - - mod_page(_('Edit tags'), 'mod/tags.html', array('board'=>$board,'token'=>make_secure_link_token('tags/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); - } - - function mod_8_reassign($b) { - global $board, $config; - - if (!openBoard($b)) - error("Could not open board!"); - - if (!hasPermission($config['mod']['reassign_board'], $b)) - error($config['error']['noaccess']); - - $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); - $mods = $query->fetchAll(); - - if (!$mods) { - error('No mods?'); - } - - $password = base64_encode(openssl_random_pseudo_bytes(9)); - $salt = generate_salt(); - $hashed = hash('sha256', $salt . sha1($password)); - - $query = prepare('UPDATE ``mods`` SET `password` = :hashed, `salt` = :salt WHERE BINARY username = :mod'); - $query->bindValue(':hashed', $hashed); - $query->bindValue(':salt', $salt); - $query->bindValue(':mod', $mods[0]['username']); - $query->execute(); - - $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at https://8ch.net/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan!"; - - modLog("Reassigned board /$b/"); - - mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); - } - - function mod_8_volunteers($b) { - global $board, $config, $pdo; - if (!hasPermission($config['mod']['edit_volunteers'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - if (isset($_POST['username'], $_POST['password'])) { - $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $count = $query->rowCount(); - $query = prepare('SELECT `username` FROM ``mods``'); - $query->execute() or error(db_error($query)); - $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); - - if ($_POST['username'] == '') - error(sprintf($config['error']['required'], 'username')); - if ($_POST['password'] == '') - error(sprintf($config['error']['required'], 'password')); - if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) - error(_('Invalid username')); - - if ($count > 10) { - error(_('Too many board volunteers!')); - } - - foreach ($volunteers as $i => $v) { - if (strtolower($_POST['username']) == strtolower($v['username'])) { - error(_('Refusing to create a volunteer with the same username as an existing one.')); - } - } - - $salt = generate_salt(); - $password = hash('sha256', $salt . sha1($_POST['password'])); - - $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, 19, :board)'); - $query->bindValue(':username', $_POST['username']); - $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - - $userID = $pdo->lastInsertId(); - - - modLog('Created a new volunteer: ' . utf8tohtml($_POST['username']) . ' (#' . $userID . ')'); - } - - if (isset($_POST['delete'])){ - foreach ($_POST['delete'] as $i => $d){ - $query = prepare('SELECT * FROM ``mods`` WHERE id = :id'); - $query->bindValue(':id', $d); - $query->execute() or error(db_error($query)); - - $result = $query->fetch(PDO::FETCH_ASSOC); - - if (!$result) { - error(_('Volunteer does not exist!')); - } - - if ($result['boards'] != $b || $result['type'] != BOARDVOLUNTEER) { - error($config['error']['noaccess']); - } - - $query = prepare('DELETE FROM ``mods`` WHERE id = :id'); - $query->bindValue(':id', $d); - $query->execute() or error(db_error($query)); - } - } - - $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); - - mod_page(_('Edit volunteers'), 'mod/volunteers.html', array('board'=>$board,'token'=>make_secure_link_token('volunteers/'.$board['uri']),'volunteers'=>$volunteers)); - - } - - function mod_8_flags($b) { - global $config, $mod, $board; - require_once 'inc/image.php'; - if (!hasPermission($config['mod']['edit_flags'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - if (file_exists("$b/flags.ser")) - $config['user_flags'] = unserialize(file_get_contents("$b/flags.ser")); - - $dir = 'static/custom-flags/'.$b; - - if (!is_dir($dir)){ - mkdir($dir, 0777, true); - } - - function handle_file($id = false, $description, $b, $dir) { - global $config; - - if (!isset($description) and $description) - error(_('You must enter a flag description!')); - - if (strlen($description) > 255) - error(_('Flag description too long!')); - - if ($id) { - $f = 'flag-'.$id; - } else { - $f = 'file'; - $id = time() . substr(microtime(), 2, 3); - } - - $upload = $_FILES[$f]['tmp_name']; - $banners = array_diff(scandir($dir), array('..', '.')); - - if (!is_readable($upload)) - error($config['error']['nomove']); - - $extension = strtolower(mb_substr($_FILES[$f]['name'], mb_strrpos($_FILES[$f]['name'], '.') + 1)); - - if ($extension != 'png') { - error(_('Flags must be in PNG format.')); - } - - if (filesize($upload) > 48000){ - error(_('File too large!')); - } - - if (!$size = @getimagesize($upload)) { - error($config['error']['invalidimg']); - } - - if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ - error(_('Image wrong size!')); - } - if (sizeof($banners) > 256) { - error(_('Too many flags.')); - } - - copy($upload, "$dir/$id.$extension"); - purge("$dir/$id.$extension"); - $config['user_flags'][$id] = utf8tohtml($description); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - // Handle a new flag, if any. - if (isset($_FILES['file'])){ - handle_file(false, $_POST['description'], $b, $dir); - } - - // Handle edits to existing flags. - foreach ($_FILES as $k => $a) { - if (empty($_FILES[$k]['tmp_name'])) continue; - - if (preg_match('/^flag-(\d+)$/', $k, $matches)) { - $id = (int)$matches[1]; - if (!isset($_POST['description-'.$id])) continue; - - if (isset($config['user_flags'][$id])) { - handle_file($id, $_POST['description-'.$id], $b, $dir); - } - } - } - - // Description just changed, flag not edited. - foreach ($_POST as $k => $v) { - if (!preg_match('/^description-(\d+)$/', $k, $matches)) continue; - $id = (int)$matches[1]; - if (!isset($_POST['description-'.$id])) continue; - - $description = $_POST['description-'.$id]; - - if (strlen($description) > 255) - error(_('Flag description too long!')); - $config['user_flags'][$id] = utf8tohtml($description); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $flags = << $d){ - if (!preg_match('/[0-9+]/', $d)){ - error('Nice try.'); - } - unlink("$dir/$d.png"); - $id = explode('.', $d)[0]; - unset($config['user_flags'][$id]); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - } - - if (isset($_POST['alphabetize'])) { - asort($config['user_flags'], SORT_NATURAL | SORT_FLAG_CASE); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - $banners = array_diff(scandir($dir), array('..', '.')); - mod_page(_('Edit flags'), 'mod/flags.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); - } - - function mod_8_banners($b) { - global $config, $mod, $board; - require_once 'inc/image.php'; - - if (!hasPermission($config['mod']['edit_banners'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - $dir = 'static/banners/'.$b; - - if (!is_dir($dir)){ - mkdir($dir, 0777, true); - } - - - if (isset($_FILES['file'])){ - $upload = $_FILES['file']['tmp_name']; - $banners = array_diff(scandir($dir), array('..', '.')); - - if (!is_readable($upload)) - error($config['error']['nomove']); - - $id = time() . substr(microtime(), 2, 3); - $extension = strtolower(mb_substr($_FILES['file']['name'], mb_strrpos($_FILES['file']['name'], '.') + 1)); - - if (!in_array($extension, array('jpg','jpeg','png','gif'))){ - error('Not an image extension.'); - } - - if (filesize($upload) > 512000){ - error('File too large!'); - } - - if (!$size = @getimagesize($upload)) { - error($config['error']['invalidimg']); - } - - if ($size[0] != 300 or $size[1] != 100){ - error('Image wrong size!'); - } - if (sizeof($banners) >= 50) { - error('Too many banners.'); - } - - copy($upload, "$dir/$id.$extension"); - } - - if (isset($_POST['delete'])){ - foreach ($_POST['delete'] as $i => $d){ - if (!preg_match('/[0-9+]\.(png|jpeg|jpg|gif)/', $d)){ - error('Nice try.'); - } - unlink("$dir/$d"); - } - } - - $banners = array_diff(scandir($dir), array('..', '.')); - mod_page(_('Edit banners'), 'mod/banners.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); - - } - - function mod_8_settings($b) { - global $config, $mod; - - //if ($b === 'infinity' && $mod['type'] !== ADMIN) - // error('Settings temporarily disabled for this board.'); - - if (!in_array($b, $mod['boards']) and $mod['boards'][0] != '*') - error($config['error']['noaccess']); - - if (!hasPermission($config['mod']['edit_settings'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - $possible_languages = array_diff(scandir('inc/locale/'), array('..', '.', '.tx', 'README.md')); - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $title = $_POST['title']; - $subtitle = $_POST['subtitle']; - $country_flags = isset($_POST['country_flags']) ? 'true' : 'false'; - $field_disable_name = isset($_POST['field_disable_name']) ? 'true' : 'false'; - $enable_embedding = isset($_POST['enable_embedding']) ? 'true' : 'false'; - $force_image_op = isset($_POST['force_image_op']) ? 'true' : 'false'; - $disable_images = isset($_POST['disable_images']) ? 'true' : 'false'; - $poster_ids = isset($_POST['poster_ids']) ? 'true' : 'false'; - $show_sages = isset($_POST['show_sages']) ? 'true' : 'false'; - $auto_unicode = isset($_POST['auto_unicode']) ? 'true' : 'false'; - $strip_combining_chars = isset($_POST['strip_combining_chars']) ? 'true' : 'false'; - $allow_roll = isset($_POST['allow_roll']) ? 'true' : 'false'; - $image_reject_repost = isset($_POST['image_reject_repost']) ? 'true' : 'false'; - $image_reject_repost_in_thread = isset($_POST['image_reject_repost_in_thread']) ? 'true' : 'false'; - $early_404 = isset($_POST['early_404']) ? 'true' : 'false'; - $allow_delete = isset($_POST['allow_delete']) ? 'true' : 'false'; - $allow_flash = isset($_POST['allow_flash']) ? '$config[\'allowed_ext_files\'][] = \'swf\';' : ''; - $allow_pdf = isset($_POST['allow_pdf']) ? '$config[\'allowed_ext_files\'][] = \'pdf\';' : ''; - $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; - $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; - $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; - $captcha = isset($_POST['captcha']) ? 'true' : 'false'; - $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; - $force_flag = isset($_POST['force_flag']) ? 'true' : 'false'; - $tor_posting = isset($_POST['tor_posting']) ? 'true' : 'false'; - $new_thread_capt = isset($_POST['new_thread_capt']) ? 'true' : 'false'; - $oekaki = isset($_POST['oekaki']) ? 'true' : 'false'; - - if ($_POST['locale'] !== 'en' && in_array($_POST['locale'], $possible_languages)) { - $locale = "\$config['locale'] = '{$_POST['locale']}.UTF-8';"; - } else { - $locale = ''; - } - - if (isset($_POST['max_images']) && (int)$_POST['max_images'] && (int)$_POST['max_images'] <= 5) { - $_POST['max_images'] = (int)$_POST['max_images']; - $multiimage = "\$config['max_images'] = {$_POST['max_images']}; - \$config['additional_javascript'][] = 'js/multi-image.js';"; - } else { - $multiimage = ''; - } - - $anonymous = base64_encode($_POST['anonymous']); - $blotter = base64_encode(purify_html(html_entity_decode($_POST['blotter']))); - $add_to_config = @file_get_contents($b.'/extra_config.php'); - $replace = ''; - - if (isset($_POST['replace'])) { - if (sizeof($_POST['replace']) > 200 || sizeof($_POST['with']) > 200) { - error(_('Sorry, max 200 wordfilters allowed.')); - } - if (count($_POST['replace']) == count($_POST['with'])) { - foreach ($_POST['replace'] as $i => $r ) { - if ($r !== '') { - $w = $_POST['with'][$i]; - - if (strlen($w) > 255) { - error(sprintf(_('Sorry, %s is too long. Max replacement is 255 characters'), utf8tohtml($w))); - } - - $replace .= '$config[\'wordfilters\'][] = array(base64_decode(\'' . base64_encode($r) . '\'), base64_decode(\'' . base64_encode($w) . '\'));'; - } - } - } - if (is_billion_laughs($_POST['replace'], $_POST['with'])) { - error(_('Wordfilters may not wordfilter previous wordfilters. For example, if a filters to bb and b filters to cc, that is not allowed.')); - } - } - - if (isset($_POST['hour_max_threads']) && in_array($_POST['hour_max_threads'], ['10', '25', '50', '100'])) { - $hour_max_threads = $_POST['hour_max_threads']; - } else { - $hour_max_threads = 'false'; - } - - if (isset($_POST['max_pages'])) { - $mp = (int)$_POST['max_pages']; - if ($mp > 25 || $mp < 2) { - $max_pages = 15; - } else { - $max_pages = $mp; - } - } else { - $max_pages = 15; - } - - if (isset($_POST['reply_limit'])) { - $rl = (int)$_POST['reply_limit']; - if ($rl > 750 || $rl < 250 || $rl % 25) { - $reply_limit = 250; - } else { - $reply_limit = $rl; - } - } else { - $reply_limit = 250; - } - - if (isset($_POST['max_newlines'])) { - $mn = (int)$_POST['max_newlines']; - if ($mn < 20 || $mn > 300) { - $max_newlines = 0; - } else { - $max_newlines = $mn; - } - } else { - $max_newlines = $mn; - } - - if (!(strlen($title) < 40)) - error('Invalid title'); - if (!(strlen($subtitle) < 200)) - error('Invalid subtitle'); - - $query = prepare('UPDATE ``boards`` SET `title` = :title, `subtitle` = :subtitle, `indexed` = :indexed, `public_bans` = :public_bans, `public_logs` = :public_logs, `8archive` = :8archive WHERE `uri` = :uri'); - $query->bindValue(':title', $title); - $query->bindValue(':subtitle', $subtitle); - $query->bindValue(':uri', $b); - $query->bindValue(':indexed', !isset($_POST['meta_noindex'])); - $query->bindValue(':public_bans', isset($_POST['public_bans'])); - $query->bindValue(':public_logs', (int)$_POST['public_logs']); - $query->bindValue(':8archive', isset($_POST['8archive'])); - $query->execute() or error(db_error($query)); - - $config_file = <<fetchAll(PDO::FETCH_ASSOC))); - file_write($b.'/config.php', $config_file); - file_write('stylesheets/board/'.$b.'.css', $clean_css); - - $_config = $config; - unset($config['wordfilters']); - - // Faster than openBoard and bypasses cache...we're trusting the PHP output - // to be safe enough to run with every request, we can eval it here. - eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); - // czaks: maybe reconsider using it, now that config is cached? - - // Clean the cache - if ($config['cache']['enabled']) { - cache::delete('board_' . $board['uri']); - cache::delete('all_boards'); - - cache::delete('config_' . $board['uri']); - cache::delete('events_' . $board['uri']); - - unlink('tmp/cache/locale_' . $board['uri']); - } - - // be smarter about rebuilds...only some changes really require us to rebuild all threads - if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] - || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ - || $_config['captcha']['extra'] != $config['captcha']['extra'] - || $_config['blotter'] != $config['blotter'] - || $_config['field_disable_name'] != $config['field_disable_name'] - || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { - buildIndex(); - $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); - while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - buildThread($post['id']); - } - } - - modLog('Edited board settings', $b); - } - - $query = prepare('SELECT * FROM boards WHERE uri = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $board = $query->fetchAll()[0]; - - $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); - - mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); - } diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 7894be58..eef96790 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -45,10 +45,636 @@ $config['mod']['shadow_capcode'] = 'Global Volunteer'; - // Mod pages code now resides in 8chan-mod-pages-functions.php file - $config['mod']['custom_pages']['/tags/(\%b)'] = '8_tags'; - $config['mod']['custom_pages']['/reassign/(\%b)'] = '8_reassign'; - $config['mod']['custom_pages']['/volunteers/(\%b)'] = '8_volunteers'; - $config['mod']['custom_pages']['/flags/(\%b)'] = '8_flags'; - $config['mod']['custom_pages']['/banners/(\%b)'] = '8_banners'; - $config['mod']['custom_pages']['/settings/(\%b)'] = '8_settings'; + function mod_8_tags ($b) { + global $board, $config; + + if (!openBoard($b)) + error("Could not open board!"); + + if (!hasPermission($config['mod']['edit_tags'], $b)) + error($config['error']['noaccess']); + + if (isset($_POST['tags'])) { + if (sizeof($_POST['tags']) > 5) + error(_('Too many tags.')); + + $delete = prepare('DELETE FROM ``board_tags`` WHERE uri = :uri'); + $delete->bindValue(':uri', $b); + $delete->execute(); + + foreach ($_POST['tags'] as $i => $tag) { + if ($tag) { + if (strlen($tag) > 255) + continue; + + $insert = prepare('INSERT INTO ``board_tags``(uri, tag) VALUES (:uri, :tag)'); + $insert->bindValue(':uri', $b); + $insert->bindValue(':tag', utf8tohtml($tag)); + $insert->execute(); + } + } + + $update = prepare('UPDATE ``boards`` SET sfw = :sfw WHERE uri = :uri'); + $update->bindValue(':uri', $b); + $update->bindValue(':sfw', isset($_POST['sfw'])); + $update->execute(); + } + $query = prepare('SELECT * FROM ``board_tags`` WHERE uri = :uri'); + $query->bindValue(':uri', $b); + $query->execute(); + + $tags = $query->fetchAll(); + + $query = prepare('SELECT `sfw` FROM ``boards`` WHERE uri = :uri'); + $query->bindValue(':uri', $b); + $query->execute(); + + $sfw = $query->fetchColumn(); + + mod_page(_('Edit tags'), 'mod/tags.html', array('board'=>$board,'token'=>make_secure_link_token('tags/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); + } + + function mod_8_reassign($b) { + global $board, $config; + + if (!openBoard($b)) + error("Could not open board!"); + + if (!hasPermission($config['mod']['reassign_board'], $b)) + error($config['error']['noaccess']); + + $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); + $mods = $query->fetchAll(); + + if (!$mods) { + error('No mods?'); + } + + $password = base64_encode(openssl_random_pseudo_bytes(9)); + $salt = generate_salt(); + $hashed = hash('sha256', $salt . sha1($password)); + + $query = prepare('UPDATE ``mods`` SET `password` = :hashed, `salt` = :salt WHERE BINARY username = :mod'); + $query->bindValue(':hashed', $hashed); + $query->bindValue(':salt', $salt); + $query->bindValue(':mod', $mods[0]['username']); + $query->execute(); + + $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at https://8ch.net/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan!"; + + modLog("Reassigned board /$b/"); + + mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); + } + + function mod_8_volunteers($b) { + global $board, $config, $pdo; + if (!hasPermission($config['mod']['edit_volunteers'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + if (isset($_POST['username'], $_POST['password'])) { + $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $count = $query->rowCount(); + $query = prepare('SELECT `username` FROM ``mods``'); + $query->execute() or error(db_error($query)); + $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); + + if ($_POST['username'] == '') + error(sprintf($config['error']['required'], 'username')); + if ($_POST['password'] == '') + error(sprintf($config['error']['required'], 'password')); + if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) + error(_('Invalid username')); + + if ($count > 10) { + error(_('Too many board volunteers!')); + } + + foreach ($volunteers as $i => $v) { + if (strtolower($_POST['username']) == strtolower($v['username'])) { + error(_('Refusing to create a volunteer with the same username as an existing one.')); + } + } + + $salt = generate_salt(); + $password = hash('sha256', $salt . sha1($_POST['password'])); + + $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, 19, :board)'); + $query->bindValue(':username', $_POST['username']); + $query->bindValue(':password', $password); + $query->bindValue(':salt', $salt); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + + $userID = $pdo->lastInsertId(); + + + modLog('Created a new volunteer: ' . utf8tohtml($_POST['username']) . ' (#' . $userID . ')'); + } + + if (isset($_POST['delete'])){ + foreach ($_POST['delete'] as $i => $d){ + $query = prepare('SELECT * FROM ``mods`` WHERE id = :id'); + $query->bindValue(':id', $d); + $query->execute() or error(db_error($query)); + + $result = $query->fetch(PDO::FETCH_ASSOC); + + if (!$result) { + error(_('Volunteer does not exist!')); + } + + if ($result['boards'] != $b || $result['type'] != BOARDVOLUNTEER) { + error($config['error']['noaccess']); + } + + $query = prepare('DELETE FROM ``mods`` WHERE id = :id'); + $query->bindValue(':id', $d); + $query->execute() or error(db_error($query)); + } + } + + $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); + + mod_page(_('Edit volunteers'), 'mod/volunteers.html', array('board'=>$board,'token'=>make_secure_link_token('volunteers/'.$board['uri']),'volunteers'=>$volunteers)); + + } + + function mod_8_flags($b) { + global $config, $mod, $board; + require_once 'inc/image.php'; + if (!hasPermission($config['mod']['edit_flags'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + if (file_exists("$b/flags.ser")) + $config['user_flags'] = unserialize(file_get_contents("$b/flags.ser")); + + $dir = 'static/custom-flags/'.$b; + + if (!is_dir($dir)){ + mkdir($dir, 0777, true); + } + + function handle_file($id = false, $description, $b, $dir) { + global $config; + + if (!isset($description) and $description) + error(_('You must enter a flag description!')); + + if (strlen($description) > 255) + error(_('Flag description too long!')); + + if ($id) { + $f = 'flag-'.$id; + } else { + $f = 'file'; + $id = time() . substr(microtime(), 2, 3); + } + + $upload = $_FILES[$f]['tmp_name']; + $banners = array_diff(scandir($dir), array('..', '.')); + + if (!is_readable($upload)) + error($config['error']['nomove']); + + $extension = strtolower(mb_substr($_FILES[$f]['name'], mb_strrpos($_FILES[$f]['name'], '.') + 1)); + + if ($extension != 'png') { + error(_('Flags must be in PNG format.')); + } + + if (filesize($upload) > 48000){ + error(_('File too large!')); + } + + if (!$size = @getimagesize($upload)) { + error($config['error']['invalidimg']); + } + + if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ + error(_('Image wrong size!')); + } + if (sizeof($banners) > 256) { + error(_('Too many flags.')); + } + + copy($upload, "$dir/$id.$extension"); + purge("$dir/$id.$extension"); + $config['user_flags'][$id] = utf8tohtml($description); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + // Handle a new flag, if any. + if (isset($_FILES['file'])){ + handle_file(false, $_POST['description'], $b, $dir); + } + + // Handle edits to existing flags. + foreach ($_FILES as $k => $a) { + if (empty($_FILES[$k]['tmp_name'])) continue; + + if (preg_match('/^flag-(\d+)$/', $k, $matches)) { + $id = (int)$matches[1]; + if (!isset($_POST['description-'.$id])) continue; + + if (isset($config['user_flags'][$id])) { + handle_file($id, $_POST['description-'.$id], $b, $dir); + } + } + } + + // Description just changed, flag not edited. + foreach ($_POST as $k => $v) { + if (!preg_match('/^description-(\d+)$/', $k, $matches)) continue; + $id = (int)$matches[1]; + if (!isset($_POST['description-'.$id])) continue; + + $description = $_POST['description-'.$id]; + + if (strlen($description) > 255) + error(_('Flag description too long!')); + $config['user_flags'][$id] = utf8tohtml($description); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $flags = << $d){ + if (!preg_match('/[0-9+]/', $d)){ + error('Nice try.'); + } + unlink("$dir/$d.png"); + $id = explode('.', $d)[0]; + unset($config['user_flags'][$id]); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + } + + if (isset($_POST['alphabetize'])) { + asort($config['user_flags'], SORT_NATURAL | SORT_FLAG_CASE); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + $banners = array_diff(scandir($dir), array('..', '.')); + mod_page(_('Edit flags'), 'mod/flags.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); + } + + function mod_8_banners($b) { + global $config, $mod, $board; + require_once 'inc/image.php'; + + if (!hasPermission($config['mod']['edit_banners'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + $dir = 'static/banners/'.$b; + + if (!is_dir($dir)){ + mkdir($dir, 0777, true); + } + + + if (isset($_FILES['file'])){ + $upload = $_FILES['file']['tmp_name']; + $banners = array_diff(scandir($dir), array('..', '.')); + + if (!is_readable($upload)) + error($config['error']['nomove']); + + $id = time() . substr(microtime(), 2, 3); + $extension = strtolower(mb_substr($_FILES['file']['name'], mb_strrpos($_FILES['file']['name'], '.') + 1)); + + if (!in_array($extension, array('jpg','jpeg','png','gif'))){ + error('Not an image extension.'); + } + + if (filesize($upload) > 512000){ + error('File too large!'); + } + + if (!$size = @getimagesize($upload)) { + error($config['error']['invalidimg']); + } + + if ($size[0] != 300 or $size[1] != 100){ + error('Image wrong size!'); + } + if (sizeof($banners) >= 50) { + error('Too many banners.'); + } + + copy($upload, "$dir/$id.$extension"); + } + + if (isset($_POST['delete'])){ + foreach ($_POST['delete'] as $i => $d){ + if (!preg_match('/[0-9+]\.(png|jpeg|jpg|gif)/', $d)){ + error('Nice try.'); + } + unlink("$dir/$d"); + } + } + + $banners = array_diff(scandir($dir), array('..', '.')); + mod_page(_('Edit banners'), 'mod/banners.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); + + } + + function mod_8_settings($b) { + global $config, $mod; + + //if ($b === 'infinity' && $mod['type'] !== ADMIN) + // error('Settings temporarily disabled for this board.'); + + if (!in_array($b, $mod['boards']) and $mod['boards'][0] != '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_settings'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + $possible_languages = array_diff(scandir('inc/locale/'), array('..', '.', '.tx', 'README.md')); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $title = $_POST['title']; + $subtitle = $_POST['subtitle']; + $country_flags = isset($_POST['country_flags']) ? 'true' : 'false'; + $field_disable_name = isset($_POST['field_disable_name']) ? 'true' : 'false'; + $enable_embedding = isset($_POST['enable_embedding']) ? 'true' : 'false'; + $force_image_op = isset($_POST['force_image_op']) ? 'true' : 'false'; + $disable_images = isset($_POST['disable_images']) ? 'true' : 'false'; + $poster_ids = isset($_POST['poster_ids']) ? 'true' : 'false'; + $show_sages = isset($_POST['show_sages']) ? 'true' : 'false'; + $auto_unicode = isset($_POST['auto_unicode']) ? 'true' : 'false'; + $strip_combining_chars = isset($_POST['strip_combining_chars']) ? 'true' : 'false'; + $allow_roll = isset($_POST['allow_roll']) ? 'true' : 'false'; + $image_reject_repost = isset($_POST['image_reject_repost']) ? 'true' : 'false'; + $image_reject_repost_in_thread = isset($_POST['image_reject_repost_in_thread']) ? 'true' : 'false'; + $early_404 = isset($_POST['early_404']) ? 'true' : 'false'; + $allow_delete = isset($_POST['allow_delete']) ? 'true' : 'false'; + $allow_flash = isset($_POST['allow_flash']) ? '$config[\'allowed_ext_files\'][] = \'swf\';' : ''; + $allow_pdf = isset($_POST['allow_pdf']) ? '$config[\'allowed_ext_files\'][] = \'pdf\';' : ''; + $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; + $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; + $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; + $captcha = isset($_POST['captcha']) ? 'true' : 'false'; + $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; + $force_flag = isset($_POST['force_flag']) ? 'true' : 'false'; + $tor_posting = isset($_POST['tor_posting']) ? 'true' : 'false'; + $new_thread_capt = isset($_POST['new_thread_capt']) ? 'true' : 'false'; + $oekaki = isset($_POST['oekaki']) ? 'true' : 'false'; + + if ($_POST['locale'] !== 'en' && in_array($_POST['locale'], $possible_languages)) { + $locale = "\$config['locale'] = '{$_POST['locale']}.UTF-8';"; + } else { + $locale = ''; + } + + if (isset($_POST['max_images']) && (int)$_POST['max_images'] && (int)$_POST['max_images'] <= 5) { + $_POST['max_images'] = (int)$_POST['max_images']; + $multiimage = "\$config['max_images'] = {$_POST['max_images']}; + \$config['additional_javascript'][] = 'js/multi-image.js';"; + } else { + $multiimage = ''; + } + + $anonymous = base64_encode($_POST['anonymous']); + $blotter = base64_encode(purify_html(html_entity_decode($_POST['blotter']))); + $add_to_config = @file_get_contents($b.'/extra_config.php'); + $replace = ''; + + if (isset($_POST['replace'])) { + if (sizeof($_POST['replace']) > 200 || sizeof($_POST['with']) > 200) { + error(_('Sorry, max 200 wordfilters allowed.')); + } + if (count($_POST['replace']) == count($_POST['with'])) { + foreach ($_POST['replace'] as $i => $r ) { + if ($r !== '') { + $w = $_POST['with'][$i]; + + if (strlen($w) > 255) { + error(sprintf(_('Sorry, %s is too long. Max replacement is 255 characters'), utf8tohtml($w))); + } + + $replace .= '$config[\'wordfilters\'][] = array(base64_decode(\'' . base64_encode($r) . '\'), base64_decode(\'' . base64_encode($w) . '\'));'; + } + } + } + if (is_billion_laughs($_POST['replace'], $_POST['with'])) { + error(_('Wordfilters may not wordfilter previous wordfilters. For example, if a filters to bb and b filters to cc, that is not allowed.')); + } + } + + if (isset($_POST['hour_max_threads']) && in_array($_POST['hour_max_threads'], ['10', '25', '50', '100'])) { + $hour_max_threads = $_POST['hour_max_threads']; + } else { + $hour_max_threads = 'false'; + } + + if (isset($_POST['max_pages'])) { + $mp = (int)$_POST['max_pages']; + if ($mp > 25 || $mp < 2) { + $max_pages = 15; + } else { + $max_pages = $mp; + } + } else { + $max_pages = 15; + } + + if (isset($_POST['reply_limit'])) { + $rl = (int)$_POST['reply_limit']; + if ($rl > 750 || $rl < 250 || $rl % 25) { + $reply_limit = 250; + } else { + $reply_limit = $rl; + } + } else { + $reply_limit = 250; + } + + if (isset($_POST['max_newlines'])) { + $mn = (int)$_POST['max_newlines']; + if ($mn < 20 || $mn > 300) { + $max_newlines = 0; + } else { + $max_newlines = $mn; + } + } else { + $max_newlines = $mn; + } + + if (!(strlen($title) < 40)) + error('Invalid title'); + if (!(strlen($subtitle) < 200)) + error('Invalid subtitle'); + + $query = prepare('UPDATE ``boards`` SET `title` = :title, `subtitle` = :subtitle, `indexed` = :indexed, `public_bans` = :public_bans, `public_logs` = :public_logs, `8archive` = :8archive WHERE `uri` = :uri'); + $query->bindValue(':title', $title); + $query->bindValue(':subtitle', $subtitle); + $query->bindValue(':uri', $b); + $query->bindValue(':indexed', !isset($_POST['meta_noindex'])); + $query->bindValue(':public_bans', isset($_POST['public_bans'])); + $query->bindValue(':public_logs', (int)$_POST['public_logs']); + $query->bindValue(':8archive', isset($_POST['8archive'])); + $query->execute() or error(db_error($query)); + + $config_file = <<fetchAll(PDO::FETCH_ASSOC))); + file_write($b.'/config.php', $config_file); + file_write('stylesheets/board/'.$b.'.css', $clean_css); + + $_config = $config; + unset($config['wordfilters']); + + // Faster than openBoard and bypasses cache...we're trusting the PHP output + // to be safe enough to run with every request, we can eval it here. + eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); + // czaks: maybe reconsider using it, now that config is cached? + + // Clean the cache + if ($config['cache']['enabled']) { + cache::delete('board_' . $board['uri']); + cache::delete('all_boards'); + + cache::delete('config_' . $board['uri']); + cache::delete('events_' . $board['uri']); + + unlink('tmp/cache/locale_' . $board['uri']); + } + + // be smarter about rebuilds...only some changes really require us to rebuild all threads + if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] + || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ + || $_config['captcha']['extra'] != $config['captcha']['extra'] + || $_config['blotter'] != $config['blotter'] + || $_config['field_disable_name'] != $config['field_disable_name'] + || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { + buildIndex(); + $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + buildThread($post['id']); + } + } + + modLog('Edited board settings', $b); + } + + $query = prepare('SELECT * FROM boards WHERE uri = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $board = $query->fetchAll()[0]; + + $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); + + mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); + } diff --git a/inc/instance-config.php b/inc/instance-config.php index 152bb9d4..4a1572a7 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -202,7 +202,7 @@ $config['twig_cache'] = false; $config['report_captcha'] = true; // 8chan specific mod pages -require '8chan-mod-pages.php'; +require '8chan-mod-config.php'; // Load database credentials require "secrets.php"; diff --git a/inc/instance-functions.php b/inc/instance-functions.php index f291714f..d3fdf24b 100644 --- a/inc/instance-functions.php +++ b/inc/instance-functions.php @@ -1,6 +1,6 @@ Date: Mon, 6 Apr 2015 22:44:48 +0200 Subject: [PATCH 38/70] infinity/smart-build: lol nano --- inc/instance-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/instance-functions.php b/inc/instance-functions.php index d3fdf24b..a2102f94 100644 --- a/inc/instance-functions.php +++ b/inc/instance-functions.php @@ -7,7 +7,7 @@ function max_posts_per_hour($post) { if (!$config['hour_max_threads']) return false; if ($post['op']) { - $query = prepare(sprintf('SELECT COUNT(*) AS `count` FROM ``posts_%s`` WHERE `thread` IS NULL AND FROM_UNIXTIME(`time`) > DATE_SUB(NOW(), INTERVAL 1 HOUR);', $board[$ + $query = prepare(sprintf('SELECT COUNT(*) AS `count` FROM ``posts_%s`` WHERE `thread` IS NULL AND FROM_UNIXTIME(`time`) > DATE_SUB(NOW(), INTERVAL 1 HOUR);', $board['uri'])); $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); $query->execute() or error(db_error($query)); $r = $query->fetch(PDO::FETCH_ASSOC); From be88d026bd2aaa66d265a29651b22f792cc957de Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 22:47:46 +0200 Subject: [PATCH 39/70] infinity/smart-build: typo --- inc/instance-config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/instance-config.php b/inc/instance-config.php index 4a1572a7..97b20491 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -177,7 +177,7 @@ $config['hour_max_threads'] = 10; $config['filters'][] = array( 'condition' => array( - 'custom' => 'test_posts_per_hour' + 'custom' => 'max_posts_per_hour' ), 'action' => 'reject', 'message' => 'On this board, to prevent raids the number of threads that can be created per hour is limited. Please try again later, or post in an existing thread.' From ab74235a4b6a1d0d3ffa4ac1f72cf4e61497a543 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 22:51:02 +0200 Subject: [PATCH 40/70] fs cache backend: silence the error --- inc/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/cache.php b/inc/cache.php index d6bc9211..852aefa2 100644 --- a/inc/cache.php +++ b/inc/cache.php @@ -132,7 +132,7 @@ class Cache { case 'fs': $key = str_replace('/', '::', $key); $key = str_replace("\0", '', $key); - unlink('tmp/cache/'.$key); + @unlink('tmp/cache/'.$key); break; case 'php': unset(self::$cache[$key]); From 1670da07cb3e9f76ba224c0c716b3c2e5f003059 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 22:55:52 +0200 Subject: [PATCH 41/70] infinity/smart-build: move a require to instance-functions --- inc/instance-config.php | 1 - inc/instance-functions.php | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/inc/instance-config.php b/inc/instance-config.php index 97b20491..529b266c 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -7,7 +7,6 @@ * * You can copy values from config.php (defaults) and paste them here. */ - require_once "lib/htmlpurifier-4.6.0/library/HTMLPurifier.auto.php"; require_once "instance-functions.php"; // Note - you may want to change some of these in secrets.php instead of here diff --git a/inc/instance-functions.php b/inc/instance-functions.php index a2102f94..9bcc7c3f 100644 --- a/inc/instance-functions.php +++ b/inc/instance-functions.php @@ -2,6 +2,9 @@ require_once("inc/8chan-functions.php"); require_once("inc/8chan-mod-pages.php"); +require_once "lib/htmlpurifier-4.6.0/library/HTMLPurifier.auto.php"; + + function max_posts_per_hour($post) { global $config, $board; if (!$config['hour_max_threads']) return false; From 8a6a20bd9c71e2c3cb48a27655643731bb63f321 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 23:54:00 +0200 Subject: [PATCH 42/70] infinity/smart-build: fix context --- inc/8chan-mod-pages.php | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index eef96790..d3a207bd 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -641,17 +641,6 @@ EOT; eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); // czaks: maybe reconsider using it, now that config is cached? - // Clean the cache - if ($config['cache']['enabled']) { - cache::delete('board_' . $board['uri']); - cache::delete('all_boards'); - - cache::delete('config_' . $board['uri']); - cache::delete('events_' . $board['uri']); - - unlink('tmp/cache/locale_' . $board['uri']); - } - // be smarter about rebuilds...only some changes really require us to rebuild all threads if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ @@ -673,6 +662,16 @@ EOT; $query->bindValue(':board', $b); $query->execute() or error(db_error($query)); $board = $query->fetchAll()[0]; + + // Clean the cache + if ($config['cache']['enabled']) { + cache::delete('board_' . $board['uri']); + cache::delete('all_boards'); + + cache::delete('config_' . $board['uri']); + cache::delete('events_' . $board['uri']); + unlink('tmp/cache/locale_' . $board['uri']); + } $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); From ef8ef2c0b43f73c09a1601739ae02c74628aa680 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 6 Apr 2015 23:58:23 +0200 Subject: [PATCH 43/70] infinity/smart-build: rebuild config cache when flags are modified --- inc/8chan-mod-pages.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index d3a207bd..9d74b8bf 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -319,6 +319,11 @@ \$config['user_flags'] = unserialize(file_get_contents('$b/flags.ser')); FLAGS; + if ($config['cache']['enabled']) { + cache::delete('config_' . $b); + cache::delete('events_' . $b); + } + file_write($b.'/flags.php', $flags); } From e3a8308e0033e6bbde6fd3bbc9536d789b473454 Mon Sep 17 00:00:00 2001 From: czaks Date: Tue, 7 Apr 2015 00:04:55 +0200 Subject: [PATCH 44/70] infinity/smart-build: fix order of loading --- inc/instance-config.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/inc/instance-config.php b/inc/instance-config.php index 529b266c..0826e894 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -7,8 +7,6 @@ * * You can copy values from config.php (defaults) and paste them here. */ - require_once "instance-functions.php"; - // Note - you may want to change some of these in secrets.php instead of here // See the secrets.example.php file $config['db']['server'] = 'localhost'; @@ -202,6 +200,9 @@ $config['report_captcha'] = true; // 8chan specific mod pages require '8chan-mod-config.php'; + +// Load instance functions later on +require_once 'instance-functions.php'; // Load database credentials require "secrets.php"; From ac693427f86731f0342f92f2b8191d5fd10374d1 Mon Sep 17 00:00:00 2001 From: czaks Date: Tue, 7 Apr 2015 00:20:07 +0200 Subject: [PATCH 45/70] infinity/smart-build: needless code duplication, fixed now --- inc/8chan-mod-config.php | 3 +-- inc/8chan-mod-pages.php | 46 ---------------------------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/inc/8chan-mod-config.php b/inc/8chan-mod-config.php index 7894be58..1c2ed2ac 100644 --- a/inc/8chan-mod-config.php +++ b/inc/8chan-mod-config.php @@ -44,8 +44,7 @@ $config['mod']['move'] = GLOBALVOLUNTEER; $config['mod']['shadow_capcode'] = 'Global Volunteer'; - - // Mod pages code now resides in 8chan-mod-pages-functions.php file + // Mod pages assignment $config['mod']['custom_pages']['/tags/(\%b)'] = '8_tags'; $config['mod']['custom_pages']['/reassign/(\%b)'] = '8_reassign'; $config['mod']['custom_pages']['/volunteers/(\%b)'] = '8_volunteers'; diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 9d74b8bf..a8e44311 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -1,50 +1,4 @@ Date: Tue, 7 Apr 2015 01:44:55 +0200 Subject: [PATCH 46/70] 8chan/smart-build: includable 404.php --- inc/instance-config.php | 3 ++- inc/instance-functions.php | 4 ++++ smart_build.php | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/inc/instance-config.php b/inc/instance-config.php index 0826e894..1701fc64 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -198,6 +198,8 @@ $config['spam']['unicode'] = false; $config['twig_cache'] = false; $config['report_captcha'] = true; +$config['page_404'] = 'page_404'; + // 8chan specific mod pages require '8chan-mod-config.php'; @@ -206,4 +208,3 @@ require_once 'instance-functions.php'; // Load database credentials require "secrets.php"; - diff --git a/inc/instance-functions.php b/inc/instance-functions.php index 9bcc7c3f..71e933d1 100644 --- a/inc/instance-functions.php +++ b/inc/instance-functions.php @@ -18,3 +18,7 @@ function max_posts_per_hour($post) { return ($r['count'] > $config['hour_max_threads']); } } + +function page_404() { + include('404.php'); +} diff --git a/smart_build.php b/smart_build.php index da1966d0..ac016480 100644 --- a/smart_build.php +++ b/smart_build.php @@ -147,6 +147,9 @@ function die_404() { global $config; header("Status: 404 Not Found"); echo "

404 Not Found

Page doesn't exist


vichan
"; } + elseif (is_callable($config['page_404'])) { + $config['page_404'](); + } else { header("Location: ".$config['page_404']); } From be80df21f0bf72210ac4b87ac6e0c773f2a6fd3a Mon Sep 17 00:00:00 2001 From: czaks Date: Tue, 7 Apr 2015 01:48:03 +0200 Subject: [PATCH 47/70] 8chan/smart-build: 404.php should global $config? php is weird --- 404.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/404.php b/404.php index e74eb65b..7422be23 100644 --- a/404.php +++ b/404.php @@ -3,6 +3,8 @@ require_once "inc/functions.php"; header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); +global $config; + $dir = "static/404/"; if (!is_dir($dir)) From d0e240acb7f736f060e5bbb0f0bd2ac0ce31f367 Mon Sep 17 00:00:00 2001 From: ninjadoge24 Date: Wed, 8 Apr 2015 21:49:03 +0530 Subject: [PATCH 48/70] Remove redundant check for existence of board It already gets checked in line 217. --- post.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/post.php b/post.php index 65e7db13..177c2dba 100644 --- a/post.php +++ b/post.php @@ -235,10 +235,6 @@ elseif (isset($_POST['post'])) { $post['thread'] = round($_POST['thread']); } else $post['op'] = true; - - // Check if board exists - if (!openBoard($post['board'])) - error($config['error']['noboard']); // Check if banned checkBan($board['uri']); From ee7c624517f52450a5959457b633143bb2c6174b Mon Sep 17 00:00:00 2001 From: 8n-tech <8n-tech@users.noreply.github.com> Date: Thu, 9 Apr 2015 20:11:48 +1000 Subject: [PATCH 49/70] Signed-off-by: 8n-tech <8n-tech@users.noreply.github.com> inc/functions.php - Fixed issues with dio on windows machines. inc/lib/Twig/Extensions/Extension/Tinyboard.php - Added strict (int) typecasting to avoid critical errors in timestamp formatting. index.php - Fixed issue where opening index.php would whitepage. static/nsfw.png static/swf.png - Added missing static files. --- inc/functions.php | 40 ++++++++++-------- .../Twig/Extensions/Extension/Tinyboard.php | 4 +- index.php | 2 + static/nsfw.png | Bin 0 -> 1229 bytes static/sfw.png | Bin 0 -> 1232 bytes 5 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 static/nsfw.png create mode 100644 static/sfw.png diff --git a/inc/functions.php b/inc/functions.php index 480df77f..4a4969ae 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -628,41 +628,47 @@ function purge($uri) { function file_write($path, $data, $simple = false, $skip_purge = false) { global $config, $debug; - + if (preg_match('/^remote:\/\/(.+)\:(.+)$/', $path, $m)) { if (isset($config['remote'][$m[1]])) { require_once 'inc/remote.php'; - + $remote = new Remote($config['remote'][$m[1]]); $remote->write($data, $m[2]); return; - } else { + } + else { error('Invalid remote server: ' . $m[1]); } } - - if (!$fp = dio_open($path, O_WRONLY | O_CREAT, 0644)) + else { + // This will convert a local, relative path like "b/index.html" to a full path. + // dio_open does not work with relative paths on Windows machines. + $path = realpath(dirname($path)) . DIRECTORY_SEPARATOR . basename($path); + } + + if (!$fp = dio_open( $path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) { error('Unable to open file for writing: ' . $path); - + } + // File locking - if (dio_fcntl($fp, F_SETLKW, array('type' => F_WRLCK)) === -1) { + if (function_exists("dio_fcntl") && dio_fcntl($fp, F_SETLKW, array('type' => F_WRLCK)) === -1) { error('Unable to lock file: ' . $path); } - - // Truncate file - if (!dio_truncate($fp, 0)) - error('Unable to truncate file: ' . $path); - + // Write data - if (($bytes = dio_write($fp, $data)) === false) + if (($bytes = dio_write($fp, $data)) === false) { error('Unable to write to file: ' . $path); - + } + // Unlock - dio_fcntl($fp, F_SETLK, array('type' => F_UNLCK)); - + if (function_exists("dio_fcntl")) { + dio_fcntl($fp, F_SETLK, array('type' => F_UNLCK)); + } + // Close dio_close($fp); - + /** * Create gzipped file. * diff --git a/inc/lib/Twig/Extensions/Extension/Tinyboard.php b/inc/lib/Twig/Extensions/Extension/Tinyboard.php index 1c5b8981..f41e0768 100644 --- a/inc/lib/Twig/Extensions/Extension/Tinyboard.php +++ b/inc/lib/Twig/Extensions/Extension/Tinyboard.php @@ -76,7 +76,7 @@ function twig_remove_whitespace_filter($data) { } function twig_date_filter($date, $format) { - return gmstrftime($format, $date); + return gmstrftime($format, (int) $date); } function twig_hasPermission_filter($mod, $permission, $board = null) { @@ -86,7 +86,7 @@ function twig_hasPermission_filter($mod, $permission, $board = null) { function twig_extension_filter($value, $case_insensitive = true) { $ext = mb_substr($value, mb_strrpos($value, '.') + 1); if($case_insensitive) - $ext = mb_strtolower($ext); + $ext = mb_strtolower($ext); return $ext; } diff --git a/index.php b/index.php index 12fec902..a44b56e5 100644 --- a/index.php +++ b/index.php @@ -9,3 +9,5 @@ if ($query) { $index = Element("8chan/index.html", array("config" => $config, "newsplus" => $newsplus)); file_write('index.html', $index); + +echo $index; \ No newline at end of file diff --git a/static/nsfw.png b/static/nsfw.png new file mode 100644 index 0000000000000000000000000000000000000000..475868b30edf938de0994384f0e3b72ebea07f78 GIT binary patch literal 1229 zcmV;;1Ty=HP)4Tx05}naRo`#hR1`jmZ&IWdKOk5~hl<6oRa0BJ8yc;~21%2p?MfD<>DVeH z9(p*dx19w`~g7O0}n_%Aq@s%d)fBDv`JHkDym6Hd+5XuAtvnwRpGmK zVkc9?T=n|PIo~X-eVh__(Z?q}P9Z-Dj?gOW6|D%o20XmjW-qs4UjrD(li^iv8@eK9k+ZFm zVRFymFOPAzG5-%Pn|1W;U4vNroTa&AxDScmEA~{ri9gr1^c?U@uwSpaNnw8l_>cP1 zd;)kMQS_;jeRSUEM_*s96y65j1$)tOrwdK{YIQMt92l|D^(E_=$Rjw{b!QT@q!)ni zR`|5oW9X5n$Wv+HVc@|^eX5yXnsHX8PF3UX~a6)MwxDE0HaPjyrlI!;jX{6Kvuh*8ej?;85ekN$?5uuCiS zBTvvVG+XTxAO{m@bvM#Jr)z6J><&E22D|vq?Y?Vkbo_DijopiF$2PET#mZ8eu=y$(ArYkv7@Ex`GL?QCc!_*KFrd&;n1r7 zqW-CFs9&fT)ZaU5gc&=gBz-DaCw(vdOp0__x+47~U6sC(E(JNe@4cTT*n6*E zVH4eoU1-&7pEV~_PRe`a7v+@vy!^5}8?Y3)UmlaER0001FNkl6a>K@I>x1Y`gIXREWCbS5&1%Q{d( z6%VtpNDp(*EHNUi!>m<&RwD;iQVtC?#jI8Q2#(?;{fVGEaXW#JQSCZ3{=sEHcL2|R rK&29A1n2rq!9ce1^a}KW+TGw65pEz5;MfpB00000NkvXXu0mjf2PH|^ literal 0 HcmV?d00001 diff --git a/static/sfw.png b/static/sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..e031f3c3f7772113a2c19b3b2b14981d23086bf0 GIT binary patch literal 1232 zcmV;>1TXuEP)4Tx05}naRo`#hR1`jmZ&IWdKOk5~hl<6oRa0BJ8yc;~21%2p?MfD<>DVeH z9(p*dx19w`~g7O0}n_%Aq@s%d)fBDv`JHkDym6Hd+5XuAtvnwRpGmK zVkc9?T=n|PIo~X-eVh__(Z?q}P9Z-Dj?gOW6|D%o20XmjW-qs4UjrD(li^iv8@eK9k+ZFm zVRFymFOPAzG5-%Pn|1W;U4vNroTa&AxDScmEA~{ri9gr1^c?U@uwSpaNnw8l_>cP1 zd;)kMQS_;jeRSUEM_*s96y65j1$)tOrwdK{YIQMt92l|D^(E_=$Rjw{b!QT@q!)ni zR`|5oW9X5n$Wv+HVc@|^eX5yXnsHX8PF3UX~a6)MwxDE0HaPjyrlI!;jX{6Kvuh*8ej?;85ekN$?5uuCiS zBTvvVG+XTxAO{m@bvM#Jr)z6J><&E22D|vq?Y?Vkbo_DijopiF$2PET#mZ8eu=y$(ArYkv7@Ex`GL?QCc!_*KFrd&;n1r7 zqW-CFs9&fT)ZaU5gc&=gBz-DaCw(vdOp0__x+47~U6sC(E(JNe@4cTT*n6*E zVH4eoU1-&7pEV~_PRe`a7v+@vy!^5}8?Y3)UmlaER0001INkl6C*TMmFQ2otmWUs+dYNk^F3H1Z+k z(XJudQDduNW=L0{#hN(FST2pyzPFaQV(kL~yWm&wUjg3X$y( Date: Mon, 13 Apr 2015 14:24:55 +1000 Subject: [PATCH 50/70] - Added board-search.php with improved board directory searching. - Added new functions to functions.php for fetching board meta. - Added new styling for non-index page 12-col layouts. - Modified templating for board directories. - Moved CSS from the index page to CSS files. Signed-off-by: 8n-tech <8n-tech@users.noreply.github.com> --- board-search.php | 162 +++++++++++++++++ boards.php | 11 +- inc/functions.php | 84 ++++++++- stylesheets/style.css | 298 ++++++++++++++++++++++++------- templates/8chan/boards-tags.html | 252 ++++++++++---------------- templates/8chan/boards.html | 102 ++++------- templates/8chan/index.html | 27 --- 7 files changed, 609 insertions(+), 327 deletions(-) create mode 100644 board-search.php diff --git a/board-search.php b/board-search.php new file mode 100644 index 00000000..d9fb3ec2 --- /dev/null +++ b/board-search.php @@ -0,0 +1,162 @@ + false, + 'nsfw' => true, + 'tags' => false, +); + +// Include NSFW boards? +if (isset( $_GET['nsfw'] )) { + $search['nsfw'] = (bool) $_GET['nsfw']; +} + +// Include what language (if the language is not blank and we recognize it)? +if (isset( $_GET['lang'] ) && isset($languages[$search['lang']])) { + $search['lang'] = $_GET['lang']; +} + +// Include what tag? +if (isset( $_GET['tags'] )) { + $search['tags'] = $_GET['tags']; +} + +/* Search boards */ +$boards = listBoards(); +$response['boards'] = array(); + +// Loop through our available boards and filter out inapplicable ones based on standard filtering. +foreach ($boards as $board) { + // Checks we can do without looking at config. + if ( + // Indexed, or we are staff, + ( $CanViewUnindexed !== true && !$board['indexed'] ) + // Not filtering NSFW, or board is SFW. + || ( $search['nsfw'] !== true && $board['sfw'] != 1 ) + ) { + continue; + } + + // Load board config. + $boardConfig = loadBoardConfig( $board['uri'] ); + + // Determine language/locale and tags. + $boardLang = strtolower( array_slice( explode( "_", $boardConfig['locale'] ?: "" ), 0 )[0] ); // en_US -> en OR en -> en + + // Check against our config search options. + if ( $search['lang'] !== false && $search['lang'] != $boardLang ) { + continue; + } + + $board['locale'] = $boardLang; + + $response['boards'][ $board['uri'] ] = $board; +} + + +/* Tag Fetching */ +// (We have do this even if we're not filtering by tags so that we know what each board's tags are) + +// Fetch all board tags for our boards. +$boardTags = fetchBoardTags( array_keys( $response['boards'] ) ); + +// Loop through each board and determine if there are tag matches. +foreach ($response['boards'] as $boardUri => &$board) { + // If we are filtering by tag and there is no match, remove from the response. + if ( $search['tags'] !== false && ( !isset( $boardTags[ $boardUri ] ) || !in_array( $search['tags'], $boardTags[ $boardUri ] )) ) { + unset( $response['boards'][$boardUri] ); + } + // If we aren't filtering / there is a match AND we have tags, set the tags. + else if ( isset( $boardTags[ $boardUri ] ) && $boardTags[ $boardUri ] ) { + $board['tags'] = $boardTags[ $boardUri ]; + } + // Othrwise, just declare our tag array blank. + else { + $board['tags'] = array(); + } +} + + +/* Activity Fetching */ +$boardActivity = fetchBoardActivity( array_keys( $response['boards'] ) ); + +// Loop through each board and record activity to it. +// We will also be weighing and building a tag list. +foreach ($response['boards'] as $boardUri => &$board) { + $board['active'] = (int) $boardActivity['active'][ $boardUri ]; + $board['pph'] = (int) $boardActivity['average'][ $boardUri ]; + + if (isset($board['tags']) && count($board['tags']) > 0) { + foreach ($board['tags'] as $tag) { + if (isset($response['tag'][$tag])) { + $response['tag'][$tag] += $board['active']; + } + else { + $response['tag'][$tag] = $board['active']; + } + } + } +} + +// Get the top most popular tags. +if (count($response['tag']) > 0) { + // Sort by most active tags. + arsort( $response['tag'] ); + // Get the first n most active tags. + $response['tag'] = array_splice( $response['tag'], 0, 200 ); + + $tagLightest = end( array_keys( $response['tag'] ) ); +} + + +/* (Please) Respond */ +$json = json_encode( $response ); + +// Error Handling +switch (json_last_error()) { + case JSON_ERROR_NONE: + $jsonError = false; + break; + case JSON_ERROR_DEPTH: + $jsonError = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $jsonError = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $jsonError = 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $jsonError = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + $jsonError = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $jsonError = 'Unknown error'; + break; +} + +if ($jsonError) { + $json = "{\"error\":\"{$jsonError}\"}"; +} + +// Successful output +echo $json; \ No newline at end of file diff --git a/boards.php b/boards.php index 87ba7ddf..a1cb260b 100644 --- a/boards.php +++ b/boards.php @@ -122,7 +122,7 @@ $body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" $html = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan")); $boards_top2k = $boards; -array_splice($boards_top2k, 2000); +array_splice($boards_top2k, 100); $boards_top2k = array_values($boards_top2k); $body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards_top2k, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags, 'top2k' => true)); $html_top2k = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan")); @@ -133,10 +133,10 @@ if ($admin) { foreach ($boards as $i => &$b) { unset($b['img']); } file_write("boards.json", json_encode($boards)); file_write("tags.json", json_encode($all_tags)); - foreach ($boards as $i => $b) { + foreach ($boards as $i => $b) {/* if (in_array($b['uri'], $config['no_top_bar_boards'])) { unset($boards[$i]); - } + }*/ unset($boards[$i]['img']); } @@ -147,6 +147,5 @@ if ($admin) { file_write("boards-top20.json", json_encode($boards)); file_write("boards.html", $html_top2k); file_write("boards_full.html", $html); - echo 'Done'; -} - + echo $html; +} \ No newline at end of file diff --git a/inc/functions.php b/inc/functions.php index 4a4969ae..1e2b9be7 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -516,9 +516,9 @@ function setupBoard($array) { $board = array( 'uri' => $array['uri'], 'title' => $array['title'], - 'subtitle' => $array['subtitle'], - 'indexed' => $array['indexed'], - 'public_logs' => $array['public_logs'] + 'subtitle' => isset($array['subtitle']) ? $array['subtitle'] : "", + 'indexed' => isset($array['indexed']) ? $array['indexed'] : true, + 'public_logs' => isset($array['public_logs']) ? $array['public_logs'] : true, ); // older versions @@ -804,6 +804,84 @@ function listBoards($just_uri = false, $indexed_only = false) { return $boards; } +function loadBoardConfig( $uri ) { + $config = array( + "locale" => "en_US", + ); + $configPath = "/{$uri}/config.php"; + + if (file_exists( $configPath ) && is_readable( $configPath )) { + include( $configPath ); + } + + // **DO NOT** use $config outside of this local scope. + // It's used by our global config array. + return $config; +} + +function fetchBoardActivity( $uris ) { + $boardActivity = array(); + + /* + $uris = "\"" . implode( (array) $uris, "\",\"" ) . "\""; + + $tagQuery = prepare("SELECT * FROM ``board_tags`` WHERE `uri` IN ({$uris})"); + $tagQuery->execute() or error(db_error($tagQuery)); + $tagResult = $tagQuery->fetchAll(PDO::FETCH_ASSOC); + + if ($tagResult) { + foreach ($tagResult as $tagRow) { + $tag = $tagRow['tag']; + $tag = trim($tag); + $tag = strtolower($tag); + $tag = str_replace(['_', ' '], '-', $tag); + + if (!isset($boardTags[ $tagRow['uri'] ])) { + $boardTags[ $tagRow['uri'] ] = array(); + } + + $boardTags[ $tagRow['uri'] ][] = htmlentities( utf8_encode( $tag ) ); + } + } + */ + + foreach( (array) $uris as $uri ) { + $random = rand( -1000, 1000 ); + if( $random < 0 ) $random = 0; + + $boardActivity['active'][ $uri ] = $random; + $boardActivity['average'][ $uri ] = $random * 72; + } + + return $boardActivity; +} + +function fetchBoardTags( $uris ) { + $boardTags = array(); + $uris = "\"" . implode( (array) $uris, "\",\"" ) . "\""; + + $tagQuery = prepare("SELECT * FROM ``board_tags`` WHERE `uri` IN ({$uris})"); + $tagQuery->execute() or error(db_error($tagQuery)); + $tagResult = $tagQuery->fetchAll(PDO::FETCH_ASSOC); + + if ($tagResult) { + foreach ($tagResult as $tagRow) { + $tag = $tagRow['tag']; + $tag = trim($tag); + $tag = strtolower($tag); + $tag = str_replace(['_', ' '], '-', $tag); + + if (!isset($boardTags[ $tagRow['uri'] ])) { + $boardTags[ $tagRow['uri'] ] = array(); + } + + $boardTags[ $tagRow['uri'] ][] = htmlentities( utf8_encode( $tag ) ); + } + } + + return $boardTags; +} + function until($timestamp) { $difference = $timestamp - time(); switch(TRUE){ diff --git a/stylesheets/style.css b/stylesheets/style.css index b5706afc..e862b9f6 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -1,3 +1,6 @@ +/* === GENERAL TAG SETTINGS === */ + +/* Page Layouts */ body { background: #EEF2FF url('img/fade-blue.png') repeat-x 50% 0%; color: black; @@ -8,6 +11,69 @@ body { padding-right: 4px; } +main, +aside, +section { + display: block; + margin: 0 auto; + width: 100%; +} + +main { + max-width: 1110px; +} + +/* Tables */ +table { + margin: auto; + width: 100%; +} + +table { + margin: auto; + width: 100%; +} +table tbody td { + margin: 0; + padding: 4px 15px 4px 4px; + + text-align: left; +} +table thead th { + border: 1px solid #000333; + padding: 4px 15px 5px 5px; + + background: #98E; + color: #000333; + text-align: left; + white-space: nowrap; +} +table tbody tr:nth-of-type( even ) { + background-color: #D6DAF0; +} + +td.minimal,th.minimal { + width: 1%; + white-space: nowrap; +} + +table.mod.config-editor { + font-size: 9pt; + width: 100%; +} + +table.mod.config-editor td { + text-align: left; + padding: 5px; + border-bottom: 1px solid #98e; +} + +table.mod.config-editor input[type="text"] { + width: 98%; +} + + +/* Uncategorized */ #post-form-outer { text-align: center; } @@ -538,50 +604,10 @@ hr { clear: left; } -div.boardlist { - color: #89A; - font-size: 9pt; - margin-top: 3px; -} - -div.boardlist.bottom { - margin-top: 20px; -} - -div.boardlist a { - text-decoration: none; -} - div.report { color: #333; } -table.modlog { - margin: auto; - width: 100%; -} - -table.modlog tr td { - text-align: left; - margin: 0; - padding: 4px 15px 0 0; -} - -table.modlog tr th { - text-align: left; - padding: 4px 15px 5px 5px; - white-space: nowrap; -} - -table.modlog tr th { - background: #98E; -} - -td.minimal,th.minimal { - width: 1%; - white-space: nowrap; -} - div.top_notice { text-align: center; margin: 5px auto; @@ -605,21 +631,6 @@ div.blotter { text-align: center; } -table.mod.config-editor { - font-size: 9pt; - width: 100%; -} - -table.mod.config-editor td { - text-align: left; - padding: 5px; - border-bottom: 1px solid #98e; -} - -table.mod.config-editor input[type="text"] { - width: 98%; -} - .desktop-style div.boardlist:not(.bottom) { position: fixed; top: 0; @@ -1012,7 +1023,6 @@ span.pln { color:grey; } - @media screen and (min-width: 768px) { p.intro { clear: none; @@ -1023,8 +1033,67 @@ span.pln { } } -/* threadwatcher */ +/* === GENERAL CLASSES === */ +.loading { + background: none; + background-color: none; + background-image: url('/static/infinity.gif'); + background-position: center center; + background-repeat: no-repeat; + min-height: 76px; + min-width: 128px; +} + +/* Responsive helpers */ +.col { + box-sizing: border-box; + float: left; +} + +.col-12 { width: 100%; } +.col-11 { width: 91.66666667%; } +.col-10 { width: 83.33333333%; } +.col-9 { width: 75%; } +.col-8 { width: 66.66666667%; } +.col-7 { width: 58.33333333%; } +.col-6 { width: 50%; } +.col-5 { width: 41.66666667%; } +.col-4 { width: 33.33333333%; } +.col-3 { width: 25%; } +.col-2 { width: 16.66666667%; } +.col-1 { width: 8.33333333%; } + +.left-push { + float: left; +} +.right-push { + float: right; +} + +/* Layout design */ +.box { + background: #D6DAF0; + border: 1px solid #000333; + color: #000333; + margin: 0 0 12px 0; +} +.box-title { + background: #98E; + color: #000333; + font-size: 120%; + font-weight: bold; + padding: 4px 8px; +} +.box-content { + padding: 0 8px; + margin: 4px 0; +} + + +/* === SPECIFIC PAGES & FEATURES === */ + +/* threadwatcher */ #watchlist { display: none; max-height: 250px; @@ -1067,25 +1136,25 @@ div.mix { } /* Mona Font */ - .aa { font-family: Mona, "MS PGothic", "MS Pゴシック", sans-serif; display: block!important; font-size: 12pt; } - -.dx,.dy,.dz { +.dx, +.dy, +.dz { width: 30px; text-align: right; display: inline-block; } +/* Dice */ .dice-option table { border: 1px dotted black; margin: 0; border-collapse: collapse; } - .dice-option table td { text-align: center; border-left: 1px dotted black; @@ -1095,7 +1164,6 @@ div.mix { } /* Quick reply (why was most of this ever in the script?) */ - #quick-reply { position: fixed; right: 5%; @@ -1261,3 +1329,109 @@ div.mix { .dropzone .remove-btn:hover { color: rgba(125, 125, 125, 1); } + +/* Board List */ +div.boardlist { + margin-top: 3px; + + color: #89A; + font-size: 9pt; +} +div.boardlist.bottom { + margin-top: 20px; +} +div.boardlist a { + text-decoration: none; +} + +table.board-list-table { + display: table; + margin: -2px; + overflow: hidden; + table-layout: fixed; +} + +table.board-list-table .board-meta { + padding-right: 4px; + width: 44px; +} +table.board-list-table .board-uri { + max-width: 196px; +} +table.board-list-table .board-title { + width: auto; +} +table.board-list-table .board-pph { + width: 55px; +} +table.board-list-table .board-max { + width: 90px; +} +table.board-list-table .board-unique { + width: 100px; +} +table.board-list-table .board-tags { + width: auto; +} + +table.board-list-table div.board-cell { + max-width: 100%; + overflow: hidden; +} + +aside.search-container { +} +aside.search-container .box { + margin-right: 12px; +} + +.board-search { + padding: 0 0 8px 0; + margin: 8px; + border-bottom: 1px solid #000333; +} +.search-item { + margin: 8px 0; +} +.search-sfw { + cursor: pointer; + font-size: 110%; + line-height: 120%; + vertical-align: bottom; +} +#search-sfw-input { + margin: 0; + padding: 0; + transform: scale(1.20); +} +#search-lang-input, +#search-tag-input { + box-sizing: border-box; + font-size: 110%; + line-height: 120%; + vertical-align: top; + padding: 2px 0 2px 4px; + max-width: 100%; + min-width: 100%; + width: 100%: +} + +ul.tag-list { + display: block; + list-style: none; + padding: 0; + margin: 0 8px; +} +ul.tag-list::after { + content: ' '; + display: block; + clear: both; +} +li.tag-item { + display: inline-block; + float: left; + font-size: 100%; + list-style: none; + margin: 0; + padding: 0 0.6em 0 0; +} \ No newline at end of file diff --git a/templates/8chan/boards-tags.html b/templates/8chan/boards-tags.html index baf1e6d0..71515f1e 100644 --- a/templates/8chan/boards-tags.html +++ b/templates/8chan/boards-tags.html @@ -1,162 +1,90 @@ - - -

{% trans %}There are currently {{n_boards}} boards + {{hidden_boards_total}} unindexed boards = {{t_boards}} total boards. Site-wide, {{total_posts_hour}} posts have been made in the last hour, with {{total_posts}} being made on all active boards since October 23, 2013.{% endtrans %}

- -{% if top2k %} -

{% trans %}This list only shows the top 2000 boards. Until we can move tag searching onto the server side, click here for the full list.{% endtrans %}

-{% endif %} - -
- Tags:  - {% for tag, pop in tags %} - {% if pop > 1000 %} - {{ tag }} - {% elseif pop > 500 %} - {{ tag }} - {% elseif pop > 100 %} - {{ tag }} - {% else %} - {{ tag }} - {% endif %} - {% endfor %} -
- - - - - - - - - - - -{% for board in boards %} - - - - - - - - -{% endfor %} -
B{% trans %}Board{% endtrans %}{% trans %}Title{% endtrans %}{% trans %}PPH{% endtrans %}{% trans %}Total posts{% endtrans %}{% trans %}Active users{% endtrans %}{% trans %}Tags{% endtrans %}
{{ board.img|raw }} {% if board['sfw'] %}{% else %}{% endif %}
/{{board['uri']}}/{{lock|raw}}
{{ board['title'] }}
{{board['pph']}}{{board['max']}}{{board['uniq_ip']}}
{% for tag in board.tags %}{{ tag }} {% endfor %}
-

Page last updated: {{last_update}}

-

{{uptime_p}} without interruption (read)

- - +
+
+

Global Statistics

+

{% trans %}There are currently {{t_boards}} total boards, {{hidden_boards_total}} of which are unindexed. Site-wide, {{total_posts_hour}} posts have been made in the last hour, with {{total_posts}} being made on all active boards since October 23, 2013.{% endtrans %}

+ {% if uptime %}

{{uptime_p}} without interruption

{% endif %} +

This page last updated .

+
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + {% for board in boards %} + + + + + + + + + + {% endfor %} + +
{% trans %}Board{% endtrans %}{% trans %}Title{% endtrans %}{% trans %}PPH{% endtrans %}{% trans %}Total posts{% endtrans %}{% trans %}Active users{% endtrans %}{% trans %}Tags{% endtrans %}
{{ board.img|raw }} {% if board['sfw'] %}{% else %}{% endif %}
/{{board['uri']}}/{{lock|raw}}
{{ board['title'] }}
{{board['pph']}}
{{board['max']}}
{{board['uniq_ip']}}
{% for tag in board.tags %}{{ tag }} {% endfor %}
+
+
+
\ No newline at end of file diff --git a/templates/8chan/boards.html b/templates/8chan/boards.html index 15fdad01..4687a6dd 100644 --- a/templates/8chan/boards.html +++ b/templates/8chan/boards.html @@ -1,68 +1,36 @@ - - -

{% trans %}There are currently {{n_boards}} boards + {{hidden_boards_total}} unindexed boards = {{t_boards}} total boards. Site-wide, {{total_posts_hour}} posts have been made in the last hour, with {{total_posts}} being made on all active boards since October 23, 2013.{% endtrans %}

- - - - - - - - - - - -{% for board in boards %} - - - - - - - - -{% endfor %} -
L{% trans %}Board{% endtrans %}{% trans %}Board title{% endtrans %}{% trans %}Posts in last hour{% endtrans %}{% trans %}Total posts{% endtrans %}{% trans %}Unique IPs{% endtrans %}{% trans %}Created{% endtrans %}
{{ board.img|raw }}/{{board['uri']}}/{{lock|raw}}{{ board['title'] }}{{board['pph']}}{{board['max']}}{{board['uniq_ip']}}{{board['time']}} ({{board['ago']}} ago)
-

Page last updated: {{last_update}}

-

{{uptime_p}} without interruption

- +
+

{% trans %}There are currently {{n_boards}} boards + {{hidden_boards_total}} unindexed boards = {{t_boards}} total boards. Site-wide, {{total_posts_hour}} posts have been made in the last hour, with {{total_posts}} being made on all active boards since October 23, 2013.{% endtrans %}

+ + + + + + + + + + + + + + + + + + {% for board in boards %} + + + + + + + + + + {% endfor %} + +
L{% trans %}Board{% endtrans %}{% trans %}Board title{% endtrans %}{% trans %}Posts in last hour{% endtrans %}{% trans %}Total posts{% endtrans %}{% trans %}Unique IPs{% endtrans %}{% trans %}Created{% endtrans %}
{{ board.img|raw }}/{{board['uri']}}/{{lock|raw}}{{ board['title'] }}{{board['pph']}}{{board['max']}}{{board['uniq_ip']}}{{board['time']}} ({{board['ago']}} ago)
+

Page last updated: {{last_update}}

+

{{uptime_p}} without interruption

+
\ No newline at end of file diff --git a/templates/8chan/index.html b/templates/8chan/index.html index 19b42dc1..d8c6d40a 100644 --- a/templates/8chan/index.html +++ b/templates/8chan/index.html @@ -4,33 +4,6 @@ ∞chan