From fa9a3da9137457083be12f7c8409e4aafa246d28 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:10:28 -0800 Subject: [PATCH 01/14] New boards.html page now with tags --- boards.php | 33 +++++- inc/functions.php | 172 ++++++++++----------------- install.sql | 15 +++ read.php | 183 ----------------------------- templates/8chan/boards-tags.html | 157 +++++++++++++++++++++++++ templates/themes/catalog/theme.php | 30 ++--- 6 files changed, 274 insertions(+), 316 deletions(-) delete mode 100644 read.php create mode 100644 templates/8chan/boards-tags.html diff --git a/boards.php b/boards.php index f22cb524..de7f15e8 100644 --- a/boards.php +++ b/boards.php @@ -10,10 +10,17 @@ if (php_sapi_name() == 'fpm-fcgi' && !$admin) { error('Cannot be run directly.'); } $boards = listBoards(); - +$all_tags = array(); $total_posts_hour = 0; $total_posts = 0; +function to_tag($str) { + $str = trim($str); + $str = strtolower($str); + $str = str_replace(['_', ' '], '-', $str); + return $str; +} + foreach ($boards as $i => $board) { //$query = prepare(sprintf("SELECT (SELECT MAX(id) from ``posts_%s``) AS max, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS oldmax, (SELECT MAX(id) from ``posts_%s``) AS max_d, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 DAY)) AS oldmax_d, (SELECT count(id) FROM ``posts_%s``) AS count;", $board['uri'], $board['uri'], $board['uri'], $board['uri'], $board['uri'])); @@ -28,6 +35,23 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) $query->execute() or error(db_error($query)); $r = $query->fetch(PDO::FETCH_ASSOC); + $tquery = prepare("SELECT `tag` FROM ``board_tags`` WHERE `uri` = :uri"); + $tquery->execute([":uri" => $board['uri']]) or error(db_error($tquery)); + $r2 = $tquery->fetchAll(PDO::FETCH_ASSOC); + + $tags = array(); + if ($r2) { + foreach ($r2 as $ii => $t) { + $tag=to_tag($t['tag']); + $tags[] = $tag; + if (!isset($all_tags[$tag])) { + $all_tags[$tag] = (int)$r['uniq_ip']; + } else { + $all_tags[$tag] += $r['uniq_ip']; + } + } + } + $pph = $r['pph']; $ppd = $r['ppd']; @@ -38,6 +62,7 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) $boards[$i]['ppd'] = $ppd; $boards[$i]['max'] = $r['max']; $boards[$i]['uniq_ip'] = $r['uniq_ip']; + $boards[$i]['tags'] = $tags; } usort($boards, @@ -86,14 +111,18 @@ foreach ($boards as $i => &$board) { $n_boards = sizeof($boards); $t_boards = $hidden_boards_total + $n_boards; +$boards = array_values($boards); +arsort($all_tags); + $config['additional_javascript'] = array('js/jquery.min.js', 'js/jquery.tablesorter.min.js'); -$body = Element("8chan/boards.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, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'))); +$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, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags)); $html = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan")); if ($admin) { echo $html; } else { file_write("boards.json", json_encode($boards)); + file_write("tags.json", json_encode($all_tags)); foreach ($boards as $i => $b) { if (in_array($b['uri'], $config['no_top_bar_boards'])) { unset($boards[$i]); diff --git a/inc/functions.php b/inc/functions.php index f02abd3d..2ffdc222 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1099,12 +1099,6 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { $antispam_query->bindValue(':board', $board['uri']); $antispam_query->bindValue(':thread', $post['id']); $antispam_query->execute() or error(db_error($antispam_query)); - - cache::delete("thread_index_{$board['uri']}_{$post['id']}"); - cache::delete("thread_index_display_{$board['uri']}_{$post['id']}"); - cache::delete("thread_{$board['uri']}_{$post['id']}"); - cache::delete("thread_50_{$board['uri']}_{$post['id']}"); - cache::delete("catalog_{$board['uri']}"); } elseif ($query->rowCount() == 1) { // Rebuild thread $rebuild = &$post['thread']; @@ -1196,62 +1190,57 @@ function index($page, $mod=false) { return false; $threads = array(); - + while ($th = $query->fetch(PDO::FETCH_ASSOC)) { - if (($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}")) || $mod)) { - $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); + $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod); - if ($config['cache']['enabled']) { - $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); - if (isset($cached['replies'], $cached['omitted'])) { - $replies = $cached['replies']; - $omitted = $cached['omitted']; - } else { - unset($cached); - } + if ($config['cache']['enabled']) { + $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}"); + if (isset($cached['replies'], $cached['omitted'])) { + $replies = $cached['replies']; + $omitted = $cached['omitted']; + } else { + unset($cached); } - if (!isset($cached)) { - $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); - $posts->bindValue(':id', $th['id']); - $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); - $posts->execute() or error(db_error($posts)); - - $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); - - if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { - $count = numPosts($th['id']); - $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); - } else { - $omitted = false; - } - - if ($config['cache']['enabled']) - cache::set("thread_index_{$board['uri']}_{$th['id']}", array( - 'replies' => $replies, - 'omitted' => $omitted, - )); - } - - $num_images = 0; - foreach ($replies as $po) { - if ($po['num_files']) - $num_images+=$po['num_files']; - - $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); - } - - $thread->images = $num_images; - $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); - - if ($omitted) { - $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); - $thread->omitted_images = $omitted['image_count'] - $num_images; - } - - if ($config['cache']['enabled'] && !$mod) - cache::set("thread_index_display_{$board['uri']}_{$th['id']}", $thread); - } + if (!isset($cached)) { + $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); + $posts->bindValue(':id', $th['id']); + $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); + $posts->execute() or error(db_error($posts)); + + $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); + + if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { + $count = numPosts($th['id']); + $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']); + } else { + $omitted = false; + } + + if ($config['cache']['enabled']) + cache::set("thread_index_{$board['uri']}_{$th['id']}", array( + 'replies' => $replies, + 'omitted' => $omitted, + )); + } + + $num_images = 0; + foreach ($replies as $po) { + if ($po['num_files']) + $num_images+=$po['num_files']; + + $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod)); + } + + $thread->images = $num_images; + $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies); + + if ($omitted) { + $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); + $thread->omitted_images = $omitted['image_count'] - $num_images; + } + $threads[] = $thread; $body .= $thread->build(true); } @@ -1469,10 +1458,6 @@ function checkMute() { function buildIndex() { global $board, $config, $build_pages; - if ($config['use_read_php']) { - cache::delete("catalog_{$board['uri']}"); - return; - } $pages = getPages(); if (!$config['try_smarter']) @@ -1550,17 +1535,6 @@ function buildIndex() { function buildJavascript() { global $config; - if ($config['cache']['enabled']) { - if (false === strpos($config['file_script'], '/')) { - $cache_name = 'main_js'; - } else { - $b = explode('/', $config['file_script'])[0]; - $cache_name = "board_{$b}_js"; - } - - cache::delete($cache_name); - } - $script = Element('main.js', array( 'config' => $config, )); @@ -1582,14 +1556,7 @@ function buildJavascript() { $script = JSMin::minify($script); } - if ($config['cache']['enabled']) - cache::set($cache_name, $script); - - if (!$config['use_read_php']) { - file_write($config['file_script'], $script); - } else { - return $script; - } + file_write($config['file_script'], $script); } function checkDNSBL() { @@ -2023,35 +1990,27 @@ function strip_combining_chars($str) { function buildThread($id, $return = false, $mod = false) { global $board, $config, $build_pages; - if (!$return && $config['use_read_php']) { - cache::delete("thread_index_{$board['uri']}_{$id}"); - cache::delete("thread_50_{$board['uri']}_{$id}"); - cache::delete("thread_index_display_{$board['uri']}_{$id}"); - cache::delete("thread_{$board['uri']}_{$id}"); - cache::delete("catalog_{$board['uri']}"); - return; - } - $id = round($id); if (event('build-thread', $id)) return; - if (!($thread = cache::get("thread_{$board['uri']}_{$id}")) || $mod) { - unset($thread); - $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); - $query->bindValue(':id', $id, PDO::PARAM_INT); - $query->execute() or error(db_error($query)); + if ($config['cache']['enabled'] && !$mod) { + // Clear cache + cache::delete("thread_index_{$board['uri']}_{$id}"); + cache::delete("thread_{$board['uri']}_{$id}"); + } - 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)); - } + $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)); } - - if (isset($thread)) cache::set("thread_{$board['uri']}_{$id}", $thread); } // Check if any posts were found @@ -2079,7 +2038,7 @@ function buildThread($id, $return = false, $mod = false) { $build_pages[] = thread_find_page($id); // json api - if ($config['api']['enabled'] && !$config['use_read_php']) { + if ($config['api']['enabled']) { $api = new Api(); $json = json_encode($api->translateThread($thread)); $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; @@ -2105,8 +2064,7 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti if ($antibot) $antibot->reset(); - if (!$thread && ($config['cache']['enabled'] && !($thread = cache::get("thread_50_{$board['uri']}_{$id}")))) { - unset($thread); + if (!$thread) { $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id` DESC LIMIT :limit", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->bindValue(':limit', $config['noko50_count']+1, PDO::PARAM_INT); @@ -2143,8 +2101,6 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti } $thread->posts = array_reverse($thread->posts); - if ($config['cache']['enabled']) - cache::set("thread_50_{$board['uri']}_{$id}", $thread); } else { $allPosts = $thread->posts; diff --git a/install.sql b/install.sql index 9e15396a..9a03736f 100644 --- a/install.sql +++ b/install.sql @@ -67,6 +67,8 @@ CREATE TABLE IF NOT EXISTS `boards` ( `subtitle` tinytext, `indexed` boolean default true, `public_bans` boolean default true, + `8archive` boolean default false, + `sfw` boolean default false, PRIMARY KEY (`uri`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; @@ -316,6 +318,19 @@ CREATE TABLE `post_clean` ( UNIQUE KEY `clean_id_UNIQUE` (`clean_id`) ); +-- -------------------------------------------------------- + +-- +-- Table structure for table `board_tags` +-- + +CREATE TABLE `board_tags` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uri` varchar(30) DEFAULT NULL, + `tag` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +); + /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/read.php b/read.php deleted file mode 100644 index 64fee794..00000000 --- a/read.php +++ /dev/null @@ -1,183 +0,0 @@ - 'view_board', - '/(\%b)/(\d+)\.json' => 'view_api_index', - '/(\%b)/catalog\.json' => 'view_api_catalog', - '/(\%b)/threads\.json' => 'view_api_threads', - '/(\%b)/main\.js' => 'view_js', - '/main\.js' => 'view_js', - '/(\%b)/catalog(\.html)?' => 'view_catalog', - '/(\%b)/' . preg_quote($config['file_index'], '!') => 'view_board', - '/(\%b)/' . str_replace('%d', '(\d+)', preg_quote($config['file_page'], '!')) => 'view_board', - '/(\%b)/' . preg_quote($config['dir']['res'], '!') . - str_replace('%d', '(\d+)', '%d\+50(\.html)?') => 'view_thread50', - '/(\%b)/' . preg_quote($config['dir']['res'], '!') . - str_replace('%d', '(\d+)', '%d(\.html)?') => 'view_thread', -); - -$new_pages = array(); -foreach ($pages as $key => $callback) { - if (is_string($callback) && preg_match('/^secure /', $callback)) - $key .= '(/(?P[a-f0-9]{8}))?'; - $key = str_replace('\%b', '?P' . sprintf(substr($config['board_path'], 0, -1), $config['board_regex']), $key); - $new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!u'] = $callback; -} -$pages = $new_pages; - -function view_thread50($boardName, $thread) { - global $config, $mod; - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - $page = buildThread50($thread, true, false); - echo $page; -} - -function view_thread($boardName, $thread) { - global $config, $mod; - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - $page = buildThread($thread, true, false); - echo $page; -} - -function view_api_index($boardName, $page) { - global $config, $board; - - $api = new Api(); - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - $content = index($page+1); - - if (!$content) - return; - - if ($config['api']['enabled']) { - $threads = $content['threads']; - $json = json_encode($api->translatePage($threads)); - header('Content-Type: text/json'); - - echo $json; - } -} - -function APICatalog($boardName, $gen_threads = false) { - global $config; - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - header('Content-Type: text/json'); - - $catalog = array(); - $api = new Api(); - - for ($page = 1; $page <= $config['max_pages']; $page++) { - $content = index($page); - - if (!$content) - break; - - $catalog[$page-1] = $content['threads']; - } - - echo json_encode($api->translateCatalog($catalog, $gen_threads)); -} - -function view_api_catalog($boardName) { - APICatalog($boardName, false); -} - -function view_api_threads($boardName) { - APICatalog($boardName, true); -} - -function view_board($boardName, $page_no = 1) { - global $config, $mod; - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - if (!$page = index($page_no, $mod)) { - error($config['error']['404']); - } - - $page['pages'] = getPages(false); - $page['pages'][$page_no-1]['selected'] = true; - $page['btn'] = getPageButtons($page['pages'], false); - $page['mod'] = false; - $page['config'] = $config; - - echo Element('index.html', $page); -} - -function view_js($boardName = false) { - global $config; - - if ($boardName && !openBoard($boardName)) - error($config['error']['noboard']); - - if (!$boardName) { - $cache_name = 'main_js'; - } else { - $cache_name = "board_{$boardName}_js"; - } - - if (!($script = cache::get($cache_name))) { - $script = buildJavascript(); - } - - echo $script; -} - -function view_catalog($boardName) { - global $board, $config; - $_theme = 'catalog'; - - $theme = loadThemeConfig($_theme); - - if (!openBoard($boardName)) { - include '404.php'; - return; - } - - require_once $config['dir']['themes'] . '/' . $_theme . '/theme.php'; - - $catalog = $theme['build_function']('read_php', themeSettings($_theme), $board['uri']); - echo $catalog; -} - -$found = false; -foreach ($pages as $uri => $handler) { - if (preg_match($uri, $query, $matches)) { - $matches = array_slice($matches, 2); - if (is_callable($handler)) { - $found = true; - call_user_func_array($handler, $matches); - } - } -} - -if (!$found) - include '404.php'; diff --git a/templates/8chan/boards-tags.html b/templates/8chan/boards-tags.html new file mode 100644 index 00000000..7ac4bcc3 --- /dev/null +++ b/templates/8chan/boards-tags.html @@ -0,0 +1,157 @@ + + +

{% 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 %}

+ +
+ 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)

+ + diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 2f3c61f1..5b940167 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -17,29 +17,22 @@ $boards = explode(' ', $settings['boards']); } - if (!$config['use_read_php']) { - if ($action == 'all') { - foreach ($boards as $board) { - $b = new Catalog(); - $b->build($settings, $board); - } - } elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && (in_array($board, $boards) | $settings['all'])) { + if ($action == 'all') { + foreach ($boards as $board) { $b = new Catalog(); $b->build($settings, $board); } - } - if ($action == 'read_php') { + } 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(); - return $b->build($settings, $board, true); + $b->build($settings, $board); } } // Wrap functions in a class so they don't interfere with normal Tinyboard operations class Catalog { - public function build($settings, $board_name, $return = false) { + public function build($settings, $board_name) { global $config, $board; - if (!($config['cache']['enabled'] && $catalog_out = cache::get("catalog_{$board['uri']}"))) {; openBoard($board_name); $recent_images = array(); @@ -97,7 +90,7 @@ $config['additional_javascript'][] = $s; } - $catalog_out = Element('themes/catalog/catalog.html', Array( + file_write($config['dir']['home'] . $board_name . '/catalog.html', Element('themes/catalog/catalog.html', Array( 'settings' => $settings, 'config' => $config, 'boardlist' => createBoardlist(), @@ -106,15 +99,6 @@ 'stats' => $stats, 'board' => $board_name, 'link' => $config['root'] . $board['dir'] - )); - - cache::set("catalog_{$board['uri']}", $catalog_out); - } - - if ($return) { - return $catalog_out; - } else { - file_write($config['dir']['home'] . $board_name . '/catalog.html', $catalog_out); - } + ))); } }; From b9dace75633e73cbf8f4ea565786e6a1988326bf Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:11:48 -0800 Subject: [PATCH 02/14] New mod.php pages: ?/tags and ?/reassign --- inc/8chan-mod-pages.php | 85 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index dfd51658..86d91ef8 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -20,6 +20,8 @@ $config['mod']['noticeboard_post'] = GLOBALVOLUNTEER; $config['mod']['search'] = GLOBALVOLUNTEER; $config['mod']['clean_global'] = GLOBALVOLUNTEER; + $config['mod']['view_notes'] = GLOBALVOLUNTEER; + $config['mod']['create_notes'] = GLOBALVOLUNTEER; $config['mod']['debug_recent'] = ADMIN; $config['mod']['debug_antispam'] = ADMIN; $config['mod']['noticeboard_post'] = ADMIN; @@ -29,6 +31,7 @@ $config['mod']['edit_flags'] = MOD; $config['mod']['edit_settings'] = MOD; $config['mod']['edit_volunteers'] = MOD; + $config['mod']['edit_tags'] = MOD; $config['mod']['clean'] = BOARDVOLUNTEER; // new perms @@ -51,6 +54,55 @@ $config['mod']['view_ban'] = BOARDVOLUNTEER; $config['mod']['reassign_board'] = ADMIN; + $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('reassign/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); + }; + $config['mod']['custom_pages']['/reassign/(\%b)'] = function($b) { global $board, $config; @@ -435,10 +487,39 @@ $locale $add_to_config EOT; + // Clean up our CSS...no more expression() or off-site URLs. + $clean_css = preg_replace('/expression\s*\(/', '', $_POST['css']); + + // URL matcher from SO: + $match_urls = '(?xi)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))'; + + $matched = array(); + + preg_match_all("#$match_urls#im", $clean_css, $matched); + + $allowed_urls = array('https://i.imgur.com/', 'https://media.8chan.co/', 'https://a.pomf.se/', 'https://fonts.googleapis.com/', 'http://8ch.net/'); + $error = false; + + if (isset($matched[0])) { + foreach ($matched[0] as $i => $v) { + $error = true; + foreach ($allowed_urls as $ii => $url) { + if (strpos($v, $url) === 0) { + $error = false; + break; + } + } + } + } + + if ($error) { + error(_('Off-site links are not allowed in board stylesheets!')); + } + $query = query('SELECT `uri`, `title`, `subtitle` FROM ``boards`` WHERE `8archive` = TRUE'); file_write('8archive.json', json_encode($query->fetchAll(PDO::FETCH_ASSOC))); file_write($b.'/config.php', $config_file); - file_write('stylesheets/board/'.$b.'.css', $_POST['css']); + file_write('stylesheets/board/'.$b.'.css', $clean_css); file_write($b.'/rules.html', Element('page.html', array('title'=>'Rules', 'subtitle'=>'', 'config'=>$config, 'body'=>'
'.purify($_POST['rules']).'
'))); file_write($b.'/rules.txt', $_POST['rules']); @@ -446,7 +527,7 @@ 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(preg_replace('/^\<\?php$/m', '', $config_file)); + eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); // be smarter about rebuilds...only some changes really require us to rebuild all threads if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] From 8cc378eaf186310f101e38e2a9f32f9aedcaafe4 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:12:38 -0800 Subject: [PATCH 03/14] claim.html wrong account bug fix --- claim.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/claim.php b/claim.php index 4c733180..a07ccadf 100644 --- a/claim.php +++ b/claim.php @@ -16,7 +16,7 @@ function last_activity($board) { $last_activity_date->setTimestamp($row['time']); - $query = query("SELECT id, username FROM mods WHERE boards = '$board'"); + $query = query("SELECT id, username FROM mods WHERE boards = '$board' AND type = 20"); $mods = $query->fetchAll(); if ($mods) { From dd1c160d35e778aff154e059183d262f7d4c9b89 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:15:24 -0800 Subject: [PATCH 04/14] Hash masked IPs option, use less_ip in inc/bans --- inc/bans.php | 10 ++-------- inc/config.php | 1 - inc/functions.php | 12 +++++++++++- inc/instance-config.php | 1 + 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/inc/bans.php b/inc/bans.php index 14b7f939..215ff279 100644 --- a/inc/bans.php +++ b/inc/bans.php @@ -218,14 +218,8 @@ class Bans { } unset($ban['type']); if ($filter_ips || ($board_access !== false && !in_array($ban['board'], $board_access))) { - @list($ban['mask'], $subnet) = explode("/", $ban['mask']); - $ban['mask'] = preg_split("/[\.:]/", $ban['mask']); - $ban['mask'] = array_slice($ban['mask'], 0, 2); - $ban['mask'] = implode(".", $ban['mask']); - $ban['mask'] .= ".x.x"; - if (isset ($subnet)) { - $ban['mask'] .= "/$subnet"; - } + $ban['mask'] = @less_ip($ban['mask']); + $ban['masked'] = true; } diff --git a/inc/config.php b/inc/config.php index 89eaac69..056ec77c 100644 --- a/inc/config.php +++ b/inc/config.php @@ -753,7 +753,6 @@ // Allowed image file extensions. $config['allowed_ext'][] = 'jpg'; $config['allowed_ext'][] = 'jpeg'; - $config['allowed_ext'][] = 'bmp'; $config['allowed_ext'][] = 'gif'; $config['allowed_ext'][] = 'png'; // $config['allowed_ext'][] = 'svg'; diff --git a/inc/functions.php b/inc/functions.php index 2ffdc222..0ff32e37 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2427,6 +2427,8 @@ function diceRoller($post) { } function less_ip($ip) { + global $config; + $ipv6 = (strstr($ip, ':') !== false); $has_range = (strstr($ip, '/') !== false); @@ -2446,7 +2448,15 @@ function less_ip($ip) { } $final = inet_ntop($in_addr & $mask); - return str_replace(array(':0', '.0'), array(':x', '.x'), $final) . (isset($range) ? '/'.$range : ''); + $masked = str_replace(array(':0', '.0'), array(':x', '.x'), $final); + + if ($config['hash_masked_ip']) { + $masked = substr(sha1(sha1($masked) . $config['secure_trip_salt']), 0, 10); + } + + $masked .= (isset($range) ? '/'.$range : ''); + + return $masked; } function less_hostmask($hostmask) { diff --git a/inc/instance-config.php b/inc/instance-config.php index 9c442fef..bd51e227 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -192,6 +192,7 @@ ); $config['gzip_static'] = false; +$config['hash_masked_ip'] = true; // 8chan specific mod pages require '8chan-mod-pages.php'; From 58c13bd2a75e5b32c83a96d298b954c0e167457d Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:16:01 -0800 Subject: [PATCH 05/14] Better post.php allow_delete error msg --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index 50c5c8bd..1a7c5da2 100644 --- a/post.php +++ b/post.php @@ -49,7 +49,7 @@ if (isset($_POST['delete'])) { // Check if deletion enabled if (!$config['allow_delete']) - error(_('Post deletion is not allowed!')); + error(_('Users are not allowed to delete their own posts on this board.')); if (empty($delete)) error($config['error']['nodelete']); From 6413bc78f6db8a1ec1eb876e09223d80ebeb64e8 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:16:25 -0800 Subject: [PATCH 06/14] Make this footer message optional --- inc/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index 056ec77c..b54d0e0f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -884,7 +884,7 @@ $config['thread_subject_in_title'] = false; // Additional lines added to the footer of all pages. - $config['footer'][] = _('All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.'); + // $config['footer'][] = _('All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.'); // Characters used to generate a random password (with Javascript). $config['genpassword_chars'] = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+'; From 8d34866636a85c555e29c29dbd35a09355d0993d Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:17:24 -0800 Subject: [PATCH 07/14] Fix locale bugs, init_locale was not calling setlocale!? --- inc/functions.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 0ff32e37..7d0cc9a3 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -29,14 +29,16 @@ mb_internal_encoding('UTF-8'); loadConfig(); function init_locale($locale, $error='error') { - if (_setlocale(LC_ALL, $locale) === false) { - $error('The specified locale (' . $locale . ') does not exist on your platform!'); - } + if ($locale === 'en') + $locale = 'en_US.utf8'; + if (extension_loaded('gettext')) { + setlocale(LC_ALL, $locale); bindtextdomain('tinyboard', './inc/locale'); bind_textdomain_codeset('tinyboard', 'UTF-8'); textdomain('tinyboard'); } else { + _setlocale(LC_ALL, $locale); _bindtextdomain('tinyboard', './inc/locale'); _bind_textdomain_codeset('tinyboard', 'UTF-8'); _textdomain('tinyboard'); From a564bb8ecc6f5753b7d3a4660fd088c46d77a0e6 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:18:24 -0800 Subject: [PATCH 08/14] Display some important information under the post form --- stylesheets/style.css | 2 +- templates/post_form.html | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/stylesheets/style.css b/stylesheets/style.css index bebd4813..4a038483 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -819,7 +819,7 @@ pre { margin-top: 1em; } -.new-threads { +.new-threads, .board-settings { text-align: center; } diff --git a/templates/post_form.html b/templates/post_form.html index 93e6465f..1b01b93b 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -178,3 +178,15 @@ + +

+{% if not config.disable_images %} +Allowed file types: {{ config.allowed_ext|join(', ') }}{% if config.allowed_ext_files %}, {{ config.allowed_ext_files|join(', ') }}{% endif %}
+Max filesize is {{ config.max_filesize|filesize }}.
+Max image dimensions are {{ config.max_height }} x {{ config.max_width }}.
+You may upload {{ config.max_images }} per post.
+{% endif %} +{% if config.allow_roll %} +You may roll dice on this board, type "dice XdY+Z" in the email field where X is number of dice, Y is max roll and Z is modifier.
+{% endif %} +

From e7fbb7e509e92b48af8c11a68178afd9cbb896d3 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:19:02 -0800 Subject: [PATCH 09/14] Update footer, warrant canary, quote US law --- inc/instance-config.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inc/instance-config.php b/inc/instance-config.php index bd51e227..2c183707 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -138,6 +138,7 @@ $config['additional_javascript'][] = 'js/comment-toolbar.js'; $config['additional_javascript'][] = 'js/catalog-search.js'; $config['additional_javascript'][] = 'js/thread-stats.js'; + $config['additional_javascript'][] = 'js/quote-selection.js'; //$config['font_awesome_css'] = '/netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css'; @@ -153,12 +154,13 @@ $config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/bans.html', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta'), array(''=>'https://twitter.com/infinitechan')); //$config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta', 'int'), array('v', 'a', 'tg', 'fit', 'pol', 'tech', 'mu', 'co', 'sp', 'boards'), array(''=>'https://twitter.com/infinitechan')); + $config['footer'][] = 'All posts on 8chan.co are the responsibility of the individual poster and not the administration of 8chan.co, pursuant to 47 U.S.C. § 230.'; + $config['footer'][] = 'We have not been served any secret court orders and are not under any gag orders.'; $config['footer'][] = 'Contribute to 8chan.co development at github'; - $config['footer'][] = 'To make a DMCA request or report illegal content, please email admin@8chan.co or use the "Global Report" functionality on every page.'; + $config['footer'][] = 'To make a DMCA request or report illegal content, please email admin@8chan.co.'; $config['search']['enable'] = true; -//$config['debug'] = true; $config['syslog'] = true; $config['wordfilters'][] = array('\rule', ''); // 'true' means it's a regular expression From a86b07282fe4c55d49d346ee23bed1b57d836ff0 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:19:27 -0800 Subject: [PATCH 10/14] Featured -> Top 15 --- templates/8chan/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/8chan/index.html b/templates/8chan/index.html index 61cd051c..406c7fbe 100644 --- a/templates/8chan/index.html +++ b/templates/8chan/index.html @@ -235,7 +235,7 @@
{% trans %}Welcome to ∞chan, the infinitely expanding imageboard.{% endtrans %}
- {% trans %}Featured boards:{% endtrans %} + {% trans %}Top 15 boards:{% endtrans %}
@@ -295,7 +295,7 @@
-

{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed. As long as the board has unobtrusive CSS, the owner doesn't ban more than half the users, the owner logs in once every two weeks and it gets one new post every week, you will retain the board forever.{% endtrans %}

+

{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed.{% endtrans %}

{% trans %}Warning: Some boards on this site might contain content of an adult or offensive nature. Please cease use of this site if it is illegal for you to view such content. The boards on this site are made entirely by the users and do not represent the opinions of the administration of 8chan.co. In the interest of free speech, only content that directly violates the DMCA or other US laws is deleted.{% endtrans %}

{% trans %}Create your board{% endtrans %} From 3df5b86235298b6d966638d4a3d14d8f91b1d3f3 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:19:50 -0800 Subject: [PATCH 11/14] Return some important info from listBoards() --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 7d0cc9a3..2d505173 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -682,7 +682,7 @@ function listBoards($just_uri = false, $indexed_only = false) { return $boards; if (!$just_uri) { - $query = query("SELECT ``boards``.`uri` uri, ``boards``.`title` title, ``boards``.`subtitle` subtitle, ``board_create``.`time` time, ``boards``.`indexed` indexed FROM ``boards``" . ( $indexed_only ? " WHERE `indexed` = 1 " : "" ) . "LEFT JOIN ``board_create`` ON ``boards``.`uri` = ``board_create``.`uri` ORDER BY ``boards``.`uri`") or error(db_error()); + $query = query("SELECT ``boards``.`uri` uri, ``boards``.`title` title, ``boards``.`subtitle` subtitle, ``board_create``.`time` time, ``boards``.`indexed` indexed, ``boards``.`sfw` sfw FROM ``boards``" . ( $indexed_only ? " WHERE `indexed` = 1 " : "" ) . "LEFT JOIN ``board_create`` ON ``boards``.`uri` = ``board_create``.`uri` ORDER BY ``boards``.`uri`") or error(db_error()); $boards = $query->fetchAll(PDO::FETCH_ASSOC); } else { $boards = array(); From 5aa40669a7b8c0e1170e538a035879800a656461 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:20:20 -0800 Subject: [PATCH 12/14] tags in settings.html --- templates/mod/settings.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/mod/settings.html b/templates/mod/settings.html index 4d29cea7..f0f16032 100644 --- a/templates/mod/settings.html +++ b/templates/mod/settings.html @@ -60,6 +60,7 @@

{% trans %}Edit board banners{% endtrans %}

{% trans %}Edit board flags{% endtrans %}

{% trans %}Edit board volunteers{% endtrans %}

+

{% trans %}Edit board tags{% endtrans %}

From 05933f23d4a81fe5b28734f2754429f1a02a2c49 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:20:36 -0800 Subject: [PATCH 13/14] Make claim.html more discoverable --- templates/8chan/create.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/8chan/create.html b/templates/8chan/create.html index f99fa727..91ed9262 100644 --- a/templates/8chan/create.html +++ b/templates/8chan/create.html @@ -1,3 +1,4 @@ +

Did you know? Many boards with popular names have been abandoned by their owners and can be claimed. See here for a full list.

{% trans %}Default poster name{% endtrans %}
From 7e7cb6117d9e0355cd572bd58d976c74ed25c0e1 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 3 Dec 2014 00:21:11 -0800 Subject: [PATCH 14/14] we dont even use this file anymore --- expire.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expire.php b/expire.php index e24b69cf..8ca0789f 100644 --- a/expire.php +++ b/expire.php @@ -8,8 +8,8 @@ $protected = array('burgers', 'cow', 'wilno', 'cute', 'yoga'); $q = query("SELECT uri FROM boards"); $boards = $q->fetchAll(PDO::FETCH_COLUMN); $now = new DateTime(); -$ago = (new DateTime)->sub(new DateInterval('P3D')); -$mod_ago = (new DateTime)->sub(new DateInterval('P7D')); +$ago = (new DateTime)->sub(new DateInterval('P7D')); +$mod_ago = (new DateTime)->sub(new DateInterval('P14D')); // Find out the last activity for our board $delete = array();