This commit is contained in:
8chan 2014-12-03 08:20:19 +00:00
commit 2671f9f9ea
18 changed files with 403 additions and 342 deletions

View File

@ -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 &infin;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]);

View File

@ -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) {

View File

@ -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();

View File

@ -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'=>'<div class="ban">'.purify($_POST['rules']).'</div>')));
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']

View File

@ -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;
}

View File

@ -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!@#$%^&*()_+';

View File

@ -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) {

View File

@ -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('<i class="fa fa-home" title="Home"></i>' => '/', '<i class="fa fa-tags" title="Boards"></i>' => '/boards.html', '<i class="fa fa-question" title="FAQ"></i>' => '/faq.html', '<i class="fa fa-random" title="Random"></i>' => '/random.php', '<i class="fa fa-plus" title="New board"></i>' => '/create.php', '<i class="fa fa-ban" title="Public ban list"></i>' => '/bans.html', '<i class="fa fa-search" title="Search"></i>' => '/search.php', '<i class="fa fa-cog" title="Manage board"></i>' => '/mod.php', '<i class="fa fa-quote-right" title="Chat"></i>' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta'), array('<i class="fa fa-twitter" title="Twitter"></i>'=>'https://twitter.com/infinitechan'));
//$config['boards'] = array(array('<i class="fa fa-home" title="Home"></i>' => '/', '<i class="fa fa-tags" title="Boards"></i>' => '/boards.html', '<i class="fa fa-question" title="FAQ"></i>' => '/faq.html', '<i class="fa fa-random" title="Random"></i>' => '/random.php', '<i class="fa fa-plus" title="New board"></i>' => '/create.php', '<i class="fa fa-search" title="Search"></i>' => '/search.php', '<i class="fa fa-cog" title="Manage board"></i>' => '/mod.php', '<i class="fa fa-quote-right" title="Chat"></i>' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta', 'int'), array('v', 'a', 'tg', 'fit', 'pol', 'tech', 'mu', 'co', 'sp', 'boards'), array('<i class="fa fa-twitter" title="Twitter"></i>'=>'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. &sect; 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 <a href="https://github.com/ctrlcctrlv/8chan">github</a>';
$config['footer'][] = 'To make a DMCA request or report illegal content, please email <a href="mailto:admin@8chan.co">admin@8chan.co</a> or use the "Global Report" functionality on every page.';
$config['footer'][] = 'To make a DMCA request or report illegal content, please email <a href="mailto:admin@8chan.co">admin@8chan.co</a>.';
$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';

View File

@ -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 */;

View File

@ -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']);

183
read.php
View File

@ -1,183 +0,0 @@
<?php
include 'inc/functions.php';
//error_reporting(E_ALL);
//ini_set('display_errors', 1);
//var_dump(cache::get("thread_hello_1"));
$query = $_SERVER['QUERY_STRING'];
$pages = array(
'/(\%b)/?' => '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<token>[a-f0-9]{8}))?';
$key = str_replace('\%b', '?P<board>' . 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';

View File

@ -819,7 +819,7 @@ pre {
margin-top: 1em;
}
.new-threads {
.new-threads, .board-settings {
text-align: center;
}

View File

@ -0,0 +1,157 @@
<style>
th.header {
background-image: url(/static/bg.gif);
cursor: pointer;
background-repeat: no-repeat;
background-position: center right;
padding-left: 20px;
margin-left: -1px;
}
th.headerSortUp {
background-image: url(/static/asc.gif);
}
th.headerSortDown {
background-image: url(/static/desc.gif);
}
table.modlog tr td.expand-td {
position: relative;
}
table.modlog tr td.expand-td:hover div{
background-color: #FFF;
position: absolute;
width: auto;
box-shadow: 0px 0px 5px #000;
padding: 0px 0 3px;
top: 5px;
left: 0;
z-index: 100;
}
.flag-eo {
background-image: url(/static/eo.png);
}
.flag-en {
background-image: url(/static/en.png);
}
.flag-jbo {
background-image: url(/static/jbo.png);
}
.uri {
overflow: hidden; width: 75px; height: 1.1em; white-space: nowrap;
}
.tags {
overflow: hidden; width: 150px; height: 1.1em; white-space: nowrap;
}
.board-name {
overflow: hidden; width: 200px; height: 1.1em; white-space: nowrap;
}
</style>
<p style='text-align:center'>{% trans %}There are currently <strong>{{n_boards}}</strong> boards + <strong>{{hidden_boards_total}}</strong> unindexed boards = <strong>{{t_boards}}</strong> 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 %}</p>
<div style='height:100px; overflow-y:scroll' class="tags-container">
<strong class="tags-strong">Tags:</strong>&nbsp;
{% for tag, pop in tags %}
{% if pop > 1000 %}
<a class="tag" href="#" style="font-size:1.75em">{{ tag }}</a>
{% elseif pop > 500 %}
<a class="tag" href="#" style="font-size:1.5em">{{ tag }}</a>
{% elseif pop > 100 %}
<a class="tag" href="#" style="font-size:1.25em">{{ tag }}</a>
{% else %}
<a class="tag" href="#">{{ tag }}</a>
{% endif %}
{% endfor %}
</div>
<table class="modlog" style="width:auto"><thead>
<tr>
<th>B</th>
<th>{% trans %}Board{% endtrans %}</th>
<th>{% trans %}Title{% endtrans %}</th>
<th title="Posts per hour">{% trans %}PPH{% endtrans %}</th>
<th>{% trans %}Total posts{% endtrans %}</th>
<th title="Unique IPs to post in the last 72 hours">{% trans %}Active users{% endtrans %}</th>
<th>{% trans %}Tags{% endtrans %}</th>
</tr></thead><tbody>
{% for board in boards %}
<tr>
<td>{{ board.img|raw }} {% if board['sfw'] %}<img src="/static/sfw.png" title="Safe for work">{% else %}<img src="/static/nsfw.png" title="Not safe for work">{% endif %}</td>
<td><div class="uri"><a href='/{{board['uri']}}/'>/{{board['uri']}}/</a>{{lock|raw}}</div></td>
<td class="expand-td" title="Created {{board['time']}} ({{board['ago']}} ago)"><div class="board-name">{{ board['title'] }}</div></td>
<td style='text-align:right'>{{board['pph']}}</td>
<td style='text-align:right'>{{board['max']}}</td>
<td style='text-align:right'>{{board['uniq_ip']}}</td>
<td class="expand-td"><div class="tags">{% for tag in board.tags %}<span class="board-tag">{{ tag }}</span>&nbsp;{% endfor %}</div></td>
{% endfor %}
</tbody></table>
<p style='text-align:center'><em>Page last updated: {{last_update}}</em></p>
<p style='text-align:center'>{{uptime_p}} without interruption (read)</p>
<script>
$(function() {
$('table').tablesorter({sortList: [[5,1]],
textExtraction: function(node) {
childNode = node.childNodes[0];
if (!childNode) { return node.innerHTML; }
if (childNode.tagName == 'IMG') {
return childNode.getAttribute('class');
} else {
return (childNode.innerHTML ? childNode.innerHTML : childNode.textContent);
}
}
});
function filter_table(search) {
$("tbody>tr").css("display", "table-row");
if ($('#clear-selection').length === 0) {
$('.tags-strong').before('<a href="#" id="clear-selection">[clear selection]</a>');
$('#clear-selection').on('click', function(e){
e.preventDefault();
$("tbody>tr").css("display", "table-row");
window.location.hash = '';
});
}
window.location.hash = search;
var tags = $(".board-tag").filter(function() {
return $(this).text() === search;
});
$("tbody>tr").css("display", "none");
tags.parents("tr").css("display", "table-row");
}
$("a.tag").on("click", function(e) {
e.preventDefault();
filter_table($(this).text());
});
$('.tags-strong').before('<label>Filter tags: <input type="text" id="filter-tags"></label> ');
$('#filter-tags').on('keyup', function(e) {
$("a.tag").css("display", "inline-block");
var search = $(this).val();
if (!search) return;
var tags = $("a.tag").filter(function() {
return (new RegExp(search)).test($(this).text());
});
$("a.tag").css("display", "none");
tags.css("display", "inline-block");
});
if (window.location.hash) {
filter_table(window.location.hash.replace('#',''));
}
});
</script>

View File

@ -1,3 +1,4 @@
<p>Did you know? Many boards with popular names have been abandoned by their owners and can be claimed. See <a href="https://8chan.co/claim.html">here</a> for a full list.</p>
<form method="POST">
<table class="modlog" style="width:auto">
<tbody>

View File

@ -235,7 +235,7 @@
<div class="col col-9 header_text">
<em>{% trans %}Welcome to ∞chan, the infinitely expanding imageboard.{% endtrans %}</em>
<br>
<strong>{% trans %}Featured boards:{% endtrans %}</strong>
<strong>{% trans %}Top 15 boards:{% endtrans %}</strong>
<span class="sub" data-description="2"></span>
<br>
<strong>
@ -295,7 +295,7 @@
<div class="col col-9 bodyCol">
<div class="content_body">
<p>{% 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 %}</p>
<p>{% trans %}On ∞chan, you can create your own imageboard for free with no experience or programming knowledge needed.{% endtrans %}</p>
<p style="color:red">{% 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 %}</p>
<div class="mobileHide">
<a href="create.php" class="button">{% trans %}Create&nbsp;your&nbsp;board{% endtrans %}</a>

View File

@ -60,6 +60,7 @@
<p style="text-align:center"><a href="?/banners/{{board.uri}}">{% trans %}Edit board banners{% endtrans %}</a></p>
<p style="text-align:center"><a href="?/flags/{{board.uri}}">{% trans %}Edit board flags{% endtrans %}</a></p>
<p style="text-align:center"><a href="?/volunteers/{{board.uri}}">{% trans %}Edit board volunteers{% endtrans %}</a></p>
<p style="text-align:center"><a href="?/tags/{{board.uri}}">{% trans %}Edit board tags{% endtrans %}</a></p>
<table>
<tr><th>{% trans %}Default poster name{% endtrans %}</th><td><input type="text" name="anonymous" value="{{config.anonymous|e}}"></td></tr>

View File

@ -178,3 +178,15 @@
<script type="text/javascript">{% raw %}
rememberStuff();
{% endraw %}</script>
<p class="unimportant board-settings">
{% if not config.disable_images %}
Allowed file types: {{ config.allowed_ext|join(', ') }}{% if config.allowed_ext_files %}, {{ config.allowed_ext_files|join(', ') }}{% endif %}<br />
Max filesize is {{ config.max_filesize|filesize }}.<br />
Max image dimensions are {{ config.max_height }} x {{ config.max_width }}.<br />
You may upload {{ config.max_images }} per post.<br />
{% 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.<br />
{% endif %}
</p>

View File

@ -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);
}
)));
}
};