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/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) { 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(); 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'] 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..b54d0e0f 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'; @@ -885,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!@#$%^&*()_+'; diff --git a/inc/functions.php b/inc/functions.php index f02abd3d..2d505173 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'); @@ -680,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(); @@ -1099,12 +1101,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 +1192,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 +1460,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 +1537,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 +1558,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 +1992,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 +2040,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 +2066,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 +2103,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; @@ -2471,6 +2429,8 @@ function diceRoller($post) { } function less_ip($ip) { + global $config; + $ipv6 = (strstr($ip, ':') !== false); $has_range = (strstr($ip, '/') !== false); @@ -2490,7 +2450,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..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 @@ -192,6 +194,7 @@ ); $config['gzip_static'] = false; +$config['hash_masked_ip'] = true; // 8chan specific mod pages require '8chan-mod-pages.php'; 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/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']); 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/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/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/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.

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

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

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); - } + ))); } };
{% trans %}Default poster name{% endtrans %}