From fe66c51a19920378885fb790d341cc50c933b046 Mon Sep 17 00:00:00 2001 From: czaks Date: Tue, 10 Mar 2015 13:13:18 +0100 Subject: [PATCH 001/212] ... (minor fix for locales) --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 2a398ae7..f1fb07d7 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -358,7 +358,7 @@ function rebuildThemes($action, $boardname = false) { // Reload the locale if ($config['locale'] != $current_locale) { $current_locale = $config['locale']; - init_locale($config['locale'], $error); + init_locale($config['locale']); } if (PHP_SAPI === 'cli') { From 11c7c57873331d9ae3eb03e7ab07acf6b9967af7 Mon Sep 17 00:00:00 2001 From: szalwia Date: Thu, 4 Jun 2015 17:18:27 +0100 Subject: [PATCH 002/212] Fix thumbnail scaling in recent theme Use decoded JSON values to set thumbnail sizes in the recent theme --- templates/themes/recent/theme.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/themes/recent/theme.php b/templates/themes/recent/theme.php index f44e2529..55660d3d 100644 --- a/templates/themes/recent/theme.php +++ b/templates/themes/recent/theme.php @@ -79,6 +79,8 @@ } else { $post['src'] = $config['uri_thumb'] . $files[0]->thumb; + $post['thumbwidth'] = $files[0]->thumbwidth; + $post['thumbheight'] = $files[0]->thumbheight; } } From 7c3126866cd291d515ab5cc83d4635c3c95c6d85 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 06:43:22 +0200 Subject: [PATCH 003/212] ease the migration process for the previous security patch (by introducing another migration); restore php 5.4 compatibility (introducing a polyfill system) --- inc/functions.php | 2 ++ inc/mod/auth.php | 10 +++++----- inc/mod/pages.php | 18 +++++++++--------- inc/polyfill.php | 28 ++++++++++++++++++++++++++++ install.php | 4 +++- install.sql | 2 +- 6 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 inc/polyfill.php diff --git a/inc/functions.php b/inc/functions.php index 515e3e55..3f5ed3b0 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -18,6 +18,8 @@ require_once 'inc/template.php'; require_once 'inc/database.php'; require_once 'inc/events.php'; require_once 'inc/api.php'; +require_once 'inc/polyfill.php'; + if (!extension_loaded('gettext')) { require_once 'inc/lib/gettext/gettext.inc'; } diff --git a/inc/mod/auth.php b/inc/mod/auth.php index fa1a0f4f..d877b89f 100644 --- a/inc/mod/auth.php +++ b/inc/mod/auth.php @@ -76,20 +76,20 @@ function generate_salt() { function login($username, $password) { global $mod, $config; - $query = prepare("SELECT `id`, `type`, `boards`, `password`, `salt` FROM ``mods`` WHERE `username` = :username"); + $query = prepare("SELECT `id`, `type`, `boards`, `password`, `version` FROM ``mods`` WHERE `username` = :username"); $query->bindValue(':username', $username); $query->execute() or error(db_error($query)); if ($user = $query->fetch(PDO::FETCH_ASSOC)) { - list($version, $ok) = test_password($user['password'], $user['salt'], $password); + list($version, $ok) = test_password($user['password'], $user['version'], $password); if ($ok) { if ($config['password_crypt_version'] > $version) { // It's time to upgrade the password hashing method! - list ($user['salt'], $user['password']) = crypt_password($password); - $query = prepare("UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id"); + list ($user['version'], $user['password']) = crypt_password($password); + $query = prepare("UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id"); $query->bindValue(':password', $user['password']); - $query->bindValue(':salt', $user['salt']); + $query->bindValue(':version', $user['version']); $query->bindValue(':id', $user['id']); $query->execute() or error(db_error($query)); } diff --git a/inc/mod/pages.php b/inc/mod/pages.php index a07de4c7..8b6f73c4 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1734,12 +1734,12 @@ function mod_user($uid) { } if ($_POST['password'] != '') { - list($salt, $password) = crypt_password($_POST['password']); + list($version, $password) = crypt_password($_POST['password']); - $query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id'); + $query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id'); $query->bindValue(':id', $uid); $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); + $query->bindValue(':version', $version); $query->execute() or error(db_error($query)); modLog('Changed password for ' . utf8tohtml($_POST['username']) . ' (#' . $user['id'] . ')'); @@ -1760,12 +1760,12 @@ function mod_user($uid) { if (hasPermission($config['mod']['change_password']) && $uid == $mod['id'] && isset($_POST['password'])) { if ($_POST['password'] != '') { - list($salt, $password) = crypt_password($_POST['password']); + list($version, $password) = crypt_password($_POST['password']); - $query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id'); + $query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id'); $query->bindValue(':id', $uid); $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); + $query->bindValue(':version', $version); $query->execute() or error(db_error($query)); modLog('Changed own password'); @@ -1832,12 +1832,12 @@ function mod_user_new() { if (!isset($config['mod']['groups'][$type]) || $type == DISABLED) error(sprintf($config['error']['invalidfield'], 'type')); - list($salt, $password) = crypt_password($_POST['password']); + list($version, $password) = crypt_password($_POST['password']); - $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, :type, :boards)'); + $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :version, :type, :boards)'); $query->bindValue(':username', $_POST['username']); $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); + $query->bindValue(':version', $version); $query->bindValue(':type', $type); $query->bindValue(':boards', implode(',', $boards)); $query->execute() or error(db_error($query)); diff --git a/inc/polyfill.php b/inc/polyfill.php new file mode 100644 index 00000000..ac40a00a --- /dev/null +++ b/inc/polyfill.php @@ -0,0 +1,28 @@ + $i ? $i : 0]) ^ ord($theirs[$i]); + } + + return $answer === 0 && $olen === $tlen; + } +} diff --git a/install.php b/install.php index 5a2d724a..968c41de 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); diff --git a/install.sql b/install.sql index fbf220c1..7e661450 100644 --- a/install.sql +++ b/install.sql @@ -132,7 +132,7 @@ CREATE TABLE IF NOT EXISTS `mods` ( `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(30) NOT NULL, `password` varchar(256) CHARACTER SET ascii NOT NULL COMMENT 'SHA256', - `salt` varchar(64) CHARACTER SET ascii NOT NULL, + `version` varchar(64) CHARACTER SET ascii NOT NULL, `type` smallint(2) NOT NULL, `boards` text CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`id`), From 97681613274c35061bcbd8d14e03600d5fb18345 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 07:51:55 +0200 Subject: [PATCH 004/212] simplify the code a bit --- inc/display.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/inc/display.php b/inc/display.php index 90cf3434..58cb4805 100644 --- a/inc/display.php +++ b/inc/display.php @@ -94,21 +94,16 @@ function error($message, $priority = true, $debug_stuff = false) { $debug_stuff['backtrace'] = debug_backtrace(); } - // Return the bad request header, necessary for AJAX posts - // czaks: is it really so? the ajax errors only work when this is commented out - // better yet use it when ajax is disabled - if (!isset ($_POST['json_response'])) { - header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); - } - - // Is there a reason to disable this? if (isset($_POST['json_response'])) { header('Content-Type: text/json; charset=utf-8'); die(json_encode(array( 'error' => $message ))); } - + else { + header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); + } + $pw = $config['db']['password']; $debug_callback = function(&$item) use (&$debug_callback, $pw) { if (is_array($item)) { From dcf5d699bd6ce5770ebd515cc14219f3015dd63a Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 08:21:21 +0200 Subject: [PATCH 005/212] simplify the md5 execution logic --- inc/api.php | 7 ++++++- inc/config.php | 4 ++-- post.php | 46 +++++++++++++++++++++++----------------------- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/inc/api.php b/inc/api.php index 57e4d367..9483200e 100644 --- a/inc/api.php +++ b/inc/api.php @@ -92,7 +92,12 @@ class Api { $dotPos = strrpos($file->file, '.'); $apiPost['ext'] = substr($file->file, $dotPos); $apiPost['tim'] = substr($file->file, 0, $dotPos); - $apiPost['md5'] = base64_encode(hex2bin($post->filehash)); + if (isset ($file->hash) && $post->filehash) { + $apiPost['md5'] = base64_encode(hex2bin($file->hash)); + } + else if (isset ($post->filehash) && $post->filehash) { + $apiPost['md5'] = base64_encode(hex2bin($post->filehash)); + } } private function translatePost($post, $threadsPage = false) { diff --git a/inc/config.php b/inc/config.php index c0e967af..b92c4ac0 100644 --- a/inc/config.php +++ b/inc/config.php @@ -807,8 +807,8 @@ // Set this to true if you're using a BSD $config['bsd_md5'] = false; - // Set this to true if you're having problems with image duplicated error and bsd_md5 doesn't help. - $config['php_md5'] = false; + // Set this to true if you're using Linux and you can execute `md5sum` binary. + $config['gnu_md5'] = false; // Number of posts in a "View Last X Posts" page $config['noko50_count'] = 50; diff --git a/post.php b/post.php index 729170a7..5c755a3c 100644 --- a/post.php +++ b/post.php @@ -572,7 +572,12 @@ if (isset($_POST['delete'])) { if ($post['has_file']) { - $fnarray = array(); + $md5cmd = false; + if ($config['bsd_md5']) $md5cmd = 'md5 -r'; + if ($config['gnu_md5']) $md5cmd = 'md5sum'; + + $allhashes = ''; + foreach ($post['files'] as $key => &$file) { if ($post['op'] && $config['allowed_ext_op']) { if (!in_array($file['extension'], $config['allowed_ext_op'])) @@ -586,34 +591,29 @@ if (isset($_POST['delete'])) { // Truncate filename if it is too long $file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']); - if (!isset($filenames)) { - $filenames = escapeshellarg($file['tmp_name']); - } else { - $filenames .= (' ' . escapeshellarg($file['tmp_name'])); - } - - $fnarray[] = $file['tmp_name']; - $upload = $file['tmp_name']; if (!is_readable($upload)) error($config['error']['nomove']); - } - - $md5cmd = $config['bsd_md5'] ? 'md5 -r' : 'md5sum'; - if (!$config['php_md5'] && $output = shell_exec_error("cat $filenames | $md5cmd")) { - $explodedvar = explode(' ', $output); - $hash = $explodedvar[0]; - $post['filehash'] = $hash; - } elseif ($config['max_images'] === 1) { - $post['filehash'] = md5_file($upload); - } else { - $str_to_hash = ''; - foreach ($fnarray as $i => $f) { - $str_to_hash .= file_get_contents($f); + if ($md5cmd) { + $output = shell_exec_error($md5cmd . " < " . escapeshellarg($upload)); + $output = explode(' ', $output); + $hash = $output[0]; } - $post['filehash'] = md5($str_to_hash); + else { + $hash = md5_file($upload); + } + + $file['hash'] = $hash; + $allhashes .= $hash; + } + + if (count ($post['files']) == 1) { + $post['filehash'] = $hash; + } + else { + $post['filehash'] = md5($allhashes); } } From 36b78e5f98302cb8fb3d4a00d4be61d6775d74a3 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 08:40:13 +0200 Subject: [PATCH 006/212] fix for editor highlighting --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 3f5ed3b0..fd4c910d 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -138,7 +138,7 @@ function loadConfig() { $configstr .= file_get_contents($board['dir'] . '/config.php'); } $matches = array(); - preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches); + preg_match_all('/[^\/#*]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches); if ($matches && isset ($matches[2]) && $matches[2]) { $matches = $matches[2]; $config['locale'] = $matches[count($matches)-1]; From a42256b296f14c5f82889f9e222d94a416aa1924 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 08:43:34 +0200 Subject: [PATCH 007/212] locale cache: fix a bug when perms are done wrong --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index fd4c910d..462bc173 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -127,7 +127,7 @@ function loadConfig() { // So, we may store the locale in a tmp/ filesystem. if (file_exists($fn = 'tmp/cache/locale_' . $boardsuffix ) ) { - $config['locale'] = file_get_contents($fn); + $config['locale'] = @file_get_contents($fn); } else { $config['locale'] = 'en'; @@ -144,7 +144,7 @@ function loadConfig() { $config['locale'] = $matches[count($matches)-1]; } - file_put_contents($fn, $config['locale']); + @file_put_contents($fn, $config['locale']); } if ($config['locale'] != $current_locale) { From 8dac72e924d658e66c5efe3334aa8ab41d00daca Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 09:16:09 +0200 Subject: [PATCH 008/212] update installer --- install.php | 103 ++++++++++++++++++++++---------- templates/installer/config.html | 3 + 2 files changed, 73 insertions(+), 33 deletions(-) diff --git a/install.php b/install.php index 968c41de..7a146c8e 100644 --- a/install.php +++ b/install.php @@ -581,6 +581,25 @@ if (file_exists($config['has_installed'])) { die(Element('page.html', $page)); } +function create_config_from_array(&$instance_config, &$array, $prefix = '') { + foreach ($array as $name => $value) { + if (is_array($value)) { + $instance_config .= "\n"; + create_config_from_array($instance_config, $value, $prefix . '[\'' . addslashes($name) . '\']'); + $instance_config .= "\n"; + } else { + $instance_config .= ' $config' . $prefix . '[\'' . addslashes($name) . '\'] = '; + + if (is_numeric($value)) + $instance_config .= $value; + else + $instance_config .= "'" . addslashes($value) . "'"; + + $instance_config .= ";\n"; + } + } +} + if ($step == 0) { // Agreeement $page['body'] = ' @@ -614,7 +633,7 @@ if ($step == 0) { 'installed' => extension_loaded('pdo'), 'required' => true ), - 'PDO' => array( + 'GD' => array( 'installed' => extension_loaded('gd'), 'required' => true ), @@ -627,17 +646,17 @@ if ($step == 0) { $tests = array( array( 'category' => 'PHP', - 'name' => 'PHP ≥ 5.3', - 'result' => PHP_VERSION_ID >= 50300, + 'name' => 'PHP ≥ 5.4', + 'result' => PHP_VERSION_ID >= 50400, 'required' => true, - 'message' => 'vichan requires PHP 5.3 or better.', + 'message' => 'vichan requires PHP 5.4 or better.', ), array( 'category' => 'PHP', - 'name' => 'PHP ≥ 5.4', - 'result' => PHP_VERSION_ID >= 50400, + 'name' => 'PHP ≥ 5.6', + 'result' => PHP_VERSION_ID >= 50600, 'required' => false, - 'message' => 'vichan works best on PHP 5.4 or better.', + 'message' => 'vichan works best on PHP 5.6 or better.', ), array( 'category' => 'PHP', @@ -694,6 +713,7 @@ if ($step == 0) { 'result' => $can_exec && shell_exec('which convert'), 'required' => false, 'message' => '(Optional) `convert` was not found or executable; command-line ImageMagick image processing cannot be enabled.', + 'effect' => function (&$config) { $config['thumb_method'] = 'convert'; }, ), array( 'category' => 'Image processing', @@ -708,6 +728,7 @@ if ($step == 0) { 'result' => $can_exec && shell_exec('which gm'), 'required' => false, 'message' => '(Optional) `gm` was not found or executable; command-line GraphicsMagick (faster than ImageMagick) cannot be enabled.', + 'effect' => function (&$config) { $config['thumb_method'] = 'gm'; }, ), array( 'category' => 'Image processing', @@ -715,13 +736,25 @@ if ($step == 0) { 'result' => $can_exec && shell_exec('which gifsicle'), 'required' => false, 'message' => '(Optional) `gifsicle` was not found or executable; you may not use `convert+gifsicle` for better animated GIF thumbnailing.', + 'effect' => function (&$config) { if ($config['thumb_method'] == 'gm') $config['thumb_method'] = 'gm+gifsicle'; + if ($config['thumb_method'] == 'convert') $config['thumb_method'] = 'convert+gifsicle'; }, ), array( 'category' => 'Image processing', - 'name' => '`md5sum` (quick file hashing)', + 'name' => '`md5sum` (quick file hashing on GNU/Linux)', + 'prereq' => '', 'result' => $can_exec && shell_exec('echo "vichan" | md5sum') == "141225c362da02b5c359c45b665168de -\n", 'required' => false, - 'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower.', + 'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower. Ignore if not using Linux.', + 'effect' => function (&$config) { $config['gnu_md5'] = true; }, + ), + array( + 'category' => 'Image processing', + 'name' => '`/sbin/md5` (quick file hashing on BSDs)', + 'result' => $can_exec && shell_exec('echo "vichan" | /sbin/md5 -r') == "141225c362da02b5c359c45b665168de\n", + 'required' => false, + 'message' => '(Optional) `/sbin/md5` was not found or executable; file hashing for multiple images will be slower. Ignore if not using BSD.', + 'effect' => function (&$config) { $config['bsd_md5'] = true; }, ), array( 'category' => 'File permissions', @@ -737,6 +770,13 @@ if ($step == 0) { 'required' => true, 'message' => 'You must give vichan permission to create (and write to) the templates/cache directory or performance will be drastically reduced.' ), + array( + 'category' => 'File permissions', + 'name' => getcwd() . '/tmp/cache', + 'result' => is_dir('tmp/cache') && is_writable('tmp/cache'), + 'required' => true, + 'message' => 'You must give vichan permission to write to the tmp/cache directory.' + ), array( 'category' => 'File permissions', 'name' => getcwd() . '/inc/instance-config.php', @@ -760,17 +800,27 @@ if ($step == 0) { 'message' => 'vichan is still beta software and it\'s not going to come out of beta any time soon. As there are often many months between releases yet changes and bug fixes are very frequent, it\'s recommended to use the git repository to maintain your vichan installation. Using git makes upgrading much easier.' ) ); - + $config['font_awesome'] = true; + $additional_config = array(); + foreach ($tests as $test) { + if ($test['result'] && $test['effect']) { + $test['effect']($additional_config); + } + } + $more = ''; + create_config_from_array($more, $additional_config); + $_SESSION['more'] = $more; + echo Element('page.html', array( 'body' => Element('installer/check-requirements.html', array( 'extensions' => $extensions, 'tests' => $tests, - 'config' => $config + 'config' => $config, )), 'title' => 'Checking environment', - 'config' => $config + 'config' => $config, )); } elseif ($step == 2) { // Basic config @@ -781,14 +831,18 @@ if ($step == 0) { echo Element('page.html', array( 'body' => Element('installer/config.html', array( - 'config' => $config + 'config' => $config, + 'more' => $more, )), 'title' => 'Configuration', 'config' => $config )); } elseif ($step == 3) { + $more = $_POST['more']; + unset($_POST['more']); + $instance_config = -' $value) { - if (is_array($value)) { - $instance_config .= "\n"; - create_config_from_array($instance_config, $value, $prefix . '[\'' . addslashes($name) . '\']'); - $instance_config .= "\n"; - } else { - $instance_config .= ' $config' . $prefix . '[\'' . addslashes($name) . '\'] = '; - - if (is_numeric($value)) - $instance_config .= $value; - else - $instance_config .= "'" . addslashes($value) . "'"; - - $instance_config .= ";\n"; - } - } - } - create_config_from_array($instance_config, $_POST); + $instance_config .= "\n"; + $instance_config .= $more; $instance_config .= "\n"; if (@file_put_contents('inc/instance-config.php', $instance_config)) { diff --git a/templates/installer/config.html b/templates/installer/config.html index 193bb98b..973328f5 100644 --- a/templates/installer/config.html +++ b/templates/installer/config.html @@ -87,6 +87,9 @@ Miscellaneous + + +

From 985c113190f077ffe60abbf89642543851076eb9 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 09:18:36 +0200 Subject: [PATCH 009/212] ... --- install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.php b/install.php index 7a146c8e..21c573d1 100644 --- a/install.php +++ b/install.php @@ -805,7 +805,7 @@ if ($step == 0) { $additional_config = array(); foreach ($tests as $test) { - if ($test['result'] && $test['effect']) { + if ($test['result'] && isset($test['effect'])) { $test['effect']($additional_config); } } From 89fe3db55608385cb4a7ba8d7ac693d6fe75ff0a Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 09:25:47 +0200 Subject: [PATCH 010/212] ... --- install.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index 21c573d1..3aa8f56f 100644 --- a/install.php +++ b/install.php @@ -600,6 +600,8 @@ function create_config_from_array(&$instance_config, &$array, $prefix = '') { } } +session_start(); + if ($step == 0) { // Agreeement $page['body'] = ' @@ -832,7 +834,7 @@ if ($step == 0) { echo Element('page.html', array( 'body' => Element('installer/config.html', array( 'config' => $config, - 'more' => $more, + 'more' => $_SESSION['more'], )), 'title' => 'Configuration', 'config' => $config From 38bf3276e4ffd4b82c4a7bc9eb8e1cf692147bb2 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 09:39:23 +0200 Subject: [PATCH 011/212] update copyright years; remove a link to tinyboard (website is dead) --- templates/generic_page.html | 8 ++++---- templates/index.html | 8 ++++---- templates/page.html | 8 ++++---- templates/themes/basic/index.html | 8 ++++---- templates/themes/catalog/catalog.html | 8 ++++---- templates/themes/categories/news.html | 8 ++++---- templates/themes/frameset/news.html | 8 ++++---- templates/themes/recent/recent.html | 8 ++++---- templates/thread.html | 8 ++++---- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/templates/generic_page.html b/templates/generic_page.html index 965cbc2c..35b58df3 100644 --- a/templates/generic_page.html +++ b/templates/generic_page.html @@ -36,10 +36,10 @@ {% endfor %} {{ btn.next }} {{ boardlist.bottom }}

-

- Tinyboard + - vichan {{ config.version }} - -
Tinyboard Copyright © 2010-2014 Tinyboard Development Group -
vichan Copyright © 2012-2015 vichan-devel

+

- Tinyboard + + vichan {{ config.version }} - +
Tinyboard Copyright © 2010-2014 Tinyboard Development Group +
vichan Copyright © 2012-2016 vichan-devel

{% for footer in config.footer %}

{{ footer }}

{% endfor %}
diff --git a/templates/main.js b/templates/main.js index 7e3b2dc1..c2a6f6c0 100644 --- a/templates/main.js +++ b/templates/main.js @@ -115,7 +115,10 @@ var styles = { {% for stylesheet in stylesheets %}{% raw %}'{% endraw %}{{ stylesheet.name|addslashes }}{% raw %}' : '{% endraw %}{{ stylesheet.uri|addslashes }}{% raw %}', {% endraw %}{% endfor %}{% raw %} }; -var board_name = false; + +if (typeof board_name === 'undefined') { + var board_name = false; +} function changeStyle(styleName, link) { {% endraw %} @@ -159,13 +162,7 @@ function changeStyle(styleName, link) { {% endraw %} {% if config.stylesheets_board %} - {# This is such an unacceptable mess. There needs to be an easier way. #} - {# Needs fix for slugify #} - var matches = document.URL.match(/\/(\w+)\/($|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.file_index|replace({'.': '\\.'}) }}|{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }})/); {% raw %} - if (matches) { - board_name = matches[1]; - } if (!localStorage.board_stylesheets) { localStorage.board_stylesheets = '{}'; diff --git a/templates/page.html b/templates/page.html index 3522702f..816bb72b 100644 --- a/templates/page.html +++ b/templates/page.html @@ -3,7 +3,7 @@ {% include 'header.html' %} {{ title }} diff --git a/templates/themes/catalog/catalog.html b/templates/themes/catalog/catalog.html index 1011fdaf..1f5e8b05 100644 --- a/templates/themes/catalog/catalog.html +++ b/templates/themes/catalog/catalog.html @@ -4,7 +4,8 @@ {% include 'header.html' %} {{ settings.title }} diff --git a/templates/thread.html b/templates/thread.html index b7bb5f6b..506b9cc2 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -4,7 +4,9 @@ {% include 'header.html' %} From d46bf4e2f2209a38e11dad436b0abb5d89ef4448 Mon Sep 17 00:00:00 2001 From: Bui Date: Fri, 3 Oct 2014 00:12:15 +0900 Subject: [PATCH 089/212] add id to thread links --- templates/thread.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/templates/thread.html b/templates/thread.html index 506b9cc2..0899c414 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -49,8 +49,12 @@ {{ body }} {% include 'report_delete.html' %} - [{% trans %}Return{% endtrans %}] - [{% trans %}Go to top{% endtrans %}] + + + [{% trans %}Return{% endtrans %}] + [{% trans %}Go to top{% endtrans %}] + [{% trans %}Catalog{% endtrans %}] + {{ boardlist.bottom }} From 6c334a3b4494a9b1b3e9f801dab45e6e027dceed Mon Sep 17 00:00:00 2001 From: Bui Date: Fri, 3 Oct 2014 00:16:41 +0900 Subject: [PATCH 090/212] lol spaces --- templates/thread.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/thread.html b/templates/thread.html index 0899c414..c909f843 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -52,9 +52,9 @@ [{% trans %}Return{% endtrans %}] - [{% trans %}Go to top{% endtrans %}] - [{% trans %}Catalog{% endtrans %}] - + [{% trans %}Go to top{% endtrans %}] + [{% trans %}Catalog{% endtrans %}] + {{ boardlist.bottom }} From 293543878a10e1ef93990acb373f8f22ca655f64 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 12:10:50 +0200 Subject: [PATCH 091/212] backport parts of 2d6d449bd2d72, in particular html classes --- stylesheets/style.css | 2 +- templates/generic_page.html | 2 +- templates/index.html | 2 +- templates/page.html | 2 +- templates/post_thread.html | 2 +- templates/themes/basic/index.html | 2 +- templates/thread.html | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stylesheets/style.css b/stylesheets/style.css index 6b4b19d9..2c5663bd 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -666,7 +666,7 @@ pre { margin-left: -20px; } -div.thread:hover { +.theme-catalog div.thread:hover { background: #D6DAF0; border-color: #B7C5D9; } diff --git a/templates/generic_page.html b/templates/generic_page.html index 35b58df3..fbfc3c50 100644 --- a/templates/generic_page.html +++ b/templates/generic_page.html @@ -6,7 +6,7 @@ {{ board.url }} - {{ board.name }} {% endblock %} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} {% if config.url_banner %}{% endif %} diff --git a/templates/index.html b/templates/index.html index d4c56707..1eea6e6b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -16,7 +16,7 @@ {% include 'header.html' %} {{ board.url }} - {{ board.title|e }} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} diff --git a/templates/page.html b/templates/page.html index 816bb72b..296b517e 100644 --- a/templates/page.html +++ b/templates/page.html @@ -8,7 +8,7 @@ {% include 'header.html' %} {{ title }} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} diff --git a/templates/post_thread.html b/templates/post_thread.html index 61146491..72ca2998 100644 --- a/templates/post_thread.html +++ b/templates/post_thread.html @@ -1,7 +1,7 @@ {% filter remove_whitespace %} {# tabs and new lines will be ignored #} -
+
{% if not index %}{% endif %} {% include 'post/fileinfo.html' %} diff --git a/templates/themes/basic/index.html b/templates/themes/basic/index.html index 8cdaf182..9d7a2c41 100644 --- a/templates/themes/basic/index.html +++ b/templates/themes/basic/index.html @@ -11,7 +11,7 @@ {% if config.default_stylesheet.1 != '' %}{% endif %} {% if config.font_awesome %}{% endif %} - + {{ boardlist.top }}

{{ settings.title }}

diff --git a/templates/thread.html b/templates/thread.html index c909f843..425394bb 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -12,7 +12,7 @@ {% include 'header.html' %} {{ board.url }} - {% if config.thread_subject_in_title and thread.subject %}{{ thread.subject }}{% else %}{{ board.title|e }}{% endif %} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} {% if config.url_banner %}{% endif %} From b6f3d440809f87a803632cefe66a861c8e20bd9b Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 9 Nov 2014 23:24:00 -0800 Subject: [PATCH 092/212] Go to bottom link --- templates/thread.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/thread.html b/templates/thread.html index 425394bb..7f8668a3 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -32,7 +32,8 @@ {% include 'attention_bar.html' %} - + {{ config.ad.top }} From aa0d3395b181671907800cd7110c00e35d8a8a13 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 20 Dec 2014 07:02:42 -0800 Subject: [PATCH 093/212] Show first 256 chars of body in --- templates/thread.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/thread.html b/templates/thread.html index 7f8668a3..335cd610 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -10,7 +10,7 @@ </script> {% include 'header.html' %} - <title>{{ board.url }} - {% if config.thread_subject_in_title and thread.subject %}{{ thread.subject }}{% else %}{{ board.title|e }}{% endif %} + {{ board.url }} - {% if config.thread_subject_in_title and thread.subject %}{{ thread.subject }}{% else %}{{ thread.body_nomarkup[:256]|e }}{% endif %} {{ boardlist.top }} From ce62673a2cf1dd4667b75756c80c44cc5746a63b Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 16 Feb 2015 16:31:15 -0800 Subject: [PATCH 094/212] OpenGraph information in thread pages https://en.wikipedia.org/wiki/Facebook_Platform#Open_Graph_protocol --- templates/thread.html | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/templates/thread.html b/templates/thread.html index 335cd610..9051d8e0 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -10,7 +10,18 @@ {% include 'header.html' %} - {{ board.url }} - {% if config.thread_subject_in_title and thread.subject %}{{ thread.subject }}{% else %}{{ thread.body_nomarkup[:256]|e }}{% endif %} + + {% set meta_subject %}{% if config.thread_subject_in_title and thread.subject %}{{ thread.subject|e }}{% else %}{{ thread.body_nomarkup[:256]|e }}{% endif %}{% endset %} + + + + + + + {% if thread.files.0.thumb %}{% endif %} + + + {{ board.url }} - {{ meta_subject }} {{ boardlist.top }} From 3f29170f1bebba532c73e4188b15cdc7e4335c15 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 12:23:18 +0200 Subject: [PATCH 095/212] debrand 8chan; btw. the previous commit was [SECURITY] i think? --- templates/thread.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/thread.html b/templates/thread.html index 9051d8e0..4314fa5e 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -13,12 +13,12 @@ {% set meta_subject %}{% if config.thread_subject_in_title and thread.subject %}{{ thread.subject|e }}{% else %}{{ thread.body_nomarkup[:256]|e }}{% endif %}{% endset %} - + - - {% if thread.files.0.thumb %}{% endif %} + + {% if thread.files.0.thumb %}{% endif %} {{ board.url }} - {{ meta_subject }} From cb97029d0d95e54d7e78e46b26b366060fb7e547 Mon Sep 17 00:00:00 2001 From: anonfagola Date: Sun, 28 Sep 2014 10:01:17 -0700 Subject: [PATCH 096/212] Update catalog.html Changed title from being - "Catalog /board/" to "/board/ - Catalog" --- templates/themes/catalog/catalog.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/themes/catalog/catalog.html b/templates/themes/catalog/catalog.html index 1f5e8b05..226c00d3 100644 --- a/templates/themes/catalog/catalog.html +++ b/templates/themes/catalog/catalog.html @@ -8,7 +8,7 @@ , board_name = "{{ board }}"; {% include 'header.html' %} - {{ settings.title }} + {{ board }} - Catalog {{ boardlist.top }} From 632d0a76d0afc5bd7a8cc90e6625a74fe432065a Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 31 Dec 2014 22:37:19 -0800 Subject: [PATCH 097/212] Display placeholder if no file in catalog/theme.php; czaks: fix the code a bit --- inc/config.php | 2 +- templates/themes/catalog/theme.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index ad659343..21f7b5cc 100644 --- a/inc/config.php +++ b/inc/config.php @@ -766,7 +766,7 @@ // Location of thumbnail to use for spoiler images. $config['spoiler_image'] = 'static/spoiler.png'; // Location of thumbnail to use for deleted images. - // $config['image_deleted'] = 'static/deleted.png'; + $config['image_deleted'] = 'static/deleted.png'; // When a thumbnailed image is going to be the same (in dimension), just copy the entire file and use // that as a thumbnail instead of resizing/redrawing. diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 34b28e48..c7a8ee95 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -88,6 +88,8 @@ $post['file'] = $config['uri_thumb'] . $files[0]->thumb; } } + } else { + $post['file'] = $config['root'] . $config['image_deleted']; } if (empty($post['image_count'])) $post['image_count'] = 0; From ae4eb4d3d9c390d4c203a468313154aa59809e59 Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 9 Mar 2015 02:31:08 -0700 Subject: [PATCH 098/212] RSS --- templates/themes/catalog/index.rss | 19 +++++++++++++++++++ templates/themes/catalog/theme.php | 7 +++++++ 2 files changed, 26 insertions(+) create mode 100644 templates/themes/catalog/index.rss diff --git a/templates/themes/catalog/index.rss b/templates/themes/catalog/index.rss new file mode 100644 index 00000000..c187e45a --- /dev/null +++ b/templates/themes/catalog/index.rss @@ -0,0 +1,19 @@ + + + + /{{ board.uri }}/ - {{ board.title|e }} + https://8ch.net/{{ board.uri }}/ + Live feed of new threads on the 8chan board /{{ board.uri }}/ - {{ board.title|e }}. + + {% for post in recent_posts %} + + {% if post.subject %}{{ post.subject|e }}{% else %}{{ post.body_nomarkup[:256]|e }}{% endif %} + https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html + https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html + https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html + {{ post.pubdate }} + {{ post.body }} ]]> + + {% endfor %} + + diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index c7a8ee95..239d4dff 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -93,6 +93,7 @@ } if (empty($post['image_count'])) $post['image_count'] = 0; + $post['pubdate'] = date('r', $post['time']); $recent_posts[] = $post; } @@ -113,5 +114,11 @@ 'board' => $board_name, 'link' => $config['root'] . $board['dir'] ))); + + file_write($config['dir']['home'] . $board_name . '/index.rss', Element('themes/catalog/index.rss', Array( + 'config' => $config, + 'recent_posts' => $recent_posts, + 'board' => $board + ))); } }; From 6da7f4d25aed8daf14f705e2acabbac8f755fe01 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sat, 14 Mar 2015 22:03:08 -0700 Subject: [PATCH 099/212] No more country flags in --- inc/lib/Twig/Extensions/Extension/Tinyboard.php | 1 + templates/themes/catalog/index.rss | 2 +- templates/thread.html | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/inc/lib/Twig/Extensions/Extension/Tinyboard.php b/inc/lib/Twig/Extensions/Extension/Tinyboard.php index 028db438..3d964c71 100644 --- a/inc/lib/Twig/Extensions/Extension/Tinyboard.php +++ b/inc/lib/Twig/Extensions/Extension/Tinyboard.php @@ -17,6 +17,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension new Twig_SimpleFilter('extension', 'twig_extension_filter'), new Twig_SimpleFilter('sprintf', 'sprintf'), new Twig_SimpleFilter('capcode', 'capcode'), + new Twig_SimpleFilter('remove_modifiers', 'remove_modifiers'), new Twig_SimpleFilter('hasPermission', 'twig_hasPermission_filter'), new Twig_SimpleFilter('date', 'twig_date_filter'), new Twig_SimpleFilter('poster_id', 'poster_id'), diff --git a/templates/themes/catalog/index.rss b/templates/themes/catalog/index.rss index c187e45a..248b494a 100644 --- a/templates/themes/catalog/index.rss +++ b/templates/themes/catalog/index.rss @@ -7,7 +7,7 @@ <atom:link href="https://8ch.net/{{ board.uri }}/index.rss" rel="self" type="application/rss+xml"/> {% for post in recent_posts %} <item> - <title>{% if post.subject %}{{ post.subject|e }}{% else %}{{ post.body_nomarkup[:256]|e }}{% endif %} + {% if post.subject %}{{ post.subject|e }}{% else %}{{ post.body_nomarkup[:256]|remove_modifiers|e }}{% endif %} https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html diff --git a/templates/thread.html b/templates/thread.html index 4314fa5e..2ca81e2e 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -11,7 +11,7 @@ {% include 'header.html' %} - {% set meta_subject %}{% if config.thread_subject_in_title and thread.subject %}{{ thread.subject|e }}{% else %}{{ thread.body_nomarkup[:256]|e }}{% endif %}{% endset %} + {% set meta_subject %}{% if config.thread_subject_in_title and thread.subject %}{{ thread.subject|e }}{% else %}{{ thread.body_nomarkup[:256]|remove_modifiers|e }}{% endif %}{% endset %} From 7a7574bdcace8ca5526ed10fe675cbbabced6ecd Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 24 Dec 2014 06:08:19 -0800 Subject: [PATCH 100/212] SECURITY / XSS : ?/edit allowed arbitrary HTML to be added by any user thru addition of 1 This allowed ANY user with ?/edit privilege to also have raw_html regardless of whether they had $config['mod']['rawhtml'] Now, any changes to markup modifiers via ?/edit are not allowed. They are removed at read time, and before write they are removed again and the ones in the database (which should be clean...) are inserted instead. Please immediately apply this patch to your instance if you are running any version of 8chan/infinity. --- inc/functions.php | 6 +++++- inc/mod/pages.php | 20 +++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index c46807bb..85b61b2e 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1849,7 +1849,11 @@ function extract_modifiers($body) { return $modifiers; } -function markup(&$body, $track_cites = false) { +function remove_modifiers($body) { + return preg_replace('@(.+?)@usm', '', $body); +} + +function markup(&$body, $track_cites = false, $op = false) { global $board, $config, $markup_urls; $modifiers = extract_modifiers($body); diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 8380cfd3..a90fbbd6 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1473,6 +1473,15 @@ function mod_edit_post($board, $edit_raw_html, $postID) { error($config['error']['404']); if (isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['body'])) { + // Remove any modifiers they may have put in + $_POST['body'] = remove_modifiers($_POST['body']); + + // Add back modifiers in the original post + $modifiers = extract_modifiers($post['body_nomarkup']); + foreach ($modifiers as $key => $value) { + $_POST['body'] .= "$value"; + } + if ($edit_raw_html) $query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name, `email` = :email, `subject` = :subject, `body` = :body, `body_nomarkup` = :body_nomarkup WHERE `id` = :id', $board)); else @@ -1501,15 +1510,20 @@ function mod_edit_post($board, $edit_raw_html, $postID) { header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . link_for($post) . '#' . $postID, true, $config['redirect_http']); } else { + // Remove modifiers + $post['body_nomarkup'] = remove_modifiers($post['body_nomarkup']); + + $post['body_nomarkup'] = utf8tohtml($post['body_nomarkup']); + $post['body'] = utf8tohtml($post['body']); if ($config['minify_html']) { - $post['body_nomarkup'] = str_replace("\n", ' ', utf8tohtml($post['body_nomarkup'])); - $post['body'] = str_replace("\n", ' ', utf8tohtml($post['body'])); + $post['body_nomarkup'] = str_replace("\n", ' ', $post['body_nomarkup']); + $post['body'] = str_replace("\n", ' ', $post['body']); $post['body_nomarkup'] = str_replace("\r", '', $post['body_nomarkup']); $post['body'] = str_replace("\r", '', $post['body']); $post['body_nomarkup'] = str_replace("\t", ' ', $post['body_nomarkup']); $post['body'] = str_replace("\t", ' ', $post['body']); } - + mod_page(_('Edit post'), 'mod/edit_post_form.html', array('token' => $security_token, 'board' => $board, 'raw' => $edit_raw_html, 'post' => $post)); } } From 6644ff666a1c794c6536c895e322dbcb50710134 Mon Sep 17 00:00:00 2001 From: 8n-tech <8n-tech@users.noreply.github.com> Date: Sat, 18 Apr 2015 04:39:50 +1000 Subject: [PATCH 101/212] Also improved some CSS and HTML aspects of the thread layout. Signed-off-by: 8n-tech <8n-tech@users.noreply.github.com> --- stylesheets/style.css | 86 ++++++++++++++++++++++++++++++------ templates/report_delete.html | 21 +++++---- templates/thread.html | 31 ++++++++----- 3 files changed, 105 insertions(+), 33 deletions(-) diff --git a/stylesheets/style.css b/stylesheets/style.css index 2c5663bd..bb7c2634 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -468,20 +468,6 @@ hr { clear: left; } -div.boardlist { - color: #89A; - font-size: 9pt; - margin-top: 3px; -} - -div.boardlist.bottom { - margin-top: 20px; -} - -div.boardlist a { - text-decoration: none; -} - div.report { color: #333; } @@ -944,6 +930,78 @@ span.pln { } } +.clearfix { + display: block; + clear: both; + visibility: hidden; + overflow: hidden; + + font-size: 0px; + line-height: 0px; + + box-sizing: border-box; + border: none; + height: 0; + margin: 0; + padding: 0; + width: 100%; + zoom: 1; +} + +/* === SPECIFIC PAGES & FEATURES === */ + +/* Board List */ +div.boardlist { + margin-top: 3px; + + color: #89A; + font-size: 9pt; +} +div.boardlist.bottom { + margin-top: 12px; + clear: both; +} +div.boardlist a { + text-decoration: none; +} + +/* Threads */ +/* Thread Footer */ +#thread-interactions { + margin: 8px 0; + clear: both; +} +#thread-links { + float: left; +} +#thread-links > a { + padding-left: none; + padding-right: 10px; +} +#thread-quick-reply { + display: none; + position: absolute; + left: 50%; + right: 50%; + text-align: center; + width: 100px; + margin-left: -50px; +} +#thread_stats { + float: right; +} + +#post-moderation-fields { + float: right; + text-align: right; +} +#delete-fields { +} +#report-fields { +} + + + /* threadwatcher */ #watchlist { diff --git a/templates/report_delete.html b/templates/report_delete.html index 2fcb7259..e246da9e 100644 --- a/templates/report_delete.html +++ b/templates/report_delete.html @@ -1,13 +1,16 @@ -{% if config.allow_delete %} -
- {% trans %}Delete Post{% endtrans %} [ - ] +
+ {% if config.allow_delete %} +
+ {% trans %}Delete Post{% endtrans %} [ + ] -
-{% endif %} -
- +
+ {% endif %} + +
+ -
+
+
\ No newline at end of file diff --git a/templates/thread.html b/templates/thread.html index 2ca81e2e..1cbb6d20 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -24,6 +24,7 @@ {{ board.url }} - {{ meta_subject }} + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} {% if config.url_banner %}{% endif %} @@ -56,18 +57,28 @@ [{% trans %}Go to bottom{% endtrans %}]
- - {% if mod %}{% endif %} - {{ body }} - {% include 'report_delete.html' %} + + {% if mod %}{% endif %} + + {{ body }} + + + +
- - [{% trans %}Return{% endtrans %}] - [{% trans %}Go to top{% endtrans %}] - [{% trans %}Catalog{% endtrans %}] - - {{ boardlist.bottom }} {{ config.ad.bottom }} From 33ef1d2123749db43bba372cbc0aa7ab3df4839a Mon Sep 17 00:00:00 2001 From: Bui Date: Sat, 26 Sep 2015 00:17:05 +0900 Subject: [PATCH 102/212] add active page classes to body; czaks: go to bottom @ thread: fixes --- templates/index.html | 2 +- templates/page.html | 2 +- templates/themes/catalog/catalog.html | 2 +- templates/thread.html | 8 ++------ 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/templates/index.html b/templates/index.html index 1eea6e6b..d1871fc8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -16,7 +16,7 @@ {% include 'header.html' %} {{ board.url }} - {{ board.title|e }} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} diff --git a/templates/page.html b/templates/page.html index 296b517e..027ed3b9 100644 --- a/templates/page.html +++ b/templates/page.html @@ -8,7 +8,7 @@ {% include 'header.html' %} {{ title }} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} diff --git a/templates/themes/catalog/catalog.html b/templates/themes/catalog/catalog.html index 226c00d3..0626b376 100644 --- a/templates/themes/catalog/catalog.html +++ b/templates/themes/catalog/catalog.html @@ -10,7 +10,7 @@ {% include 'header.html' %} {{ board }} - Catalog - + {{ boardlist.top }}

{{ settings.title }} (/{{ board }}/)

diff --git a/templates/thread.html b/templates/thread.html index 1cbb6d20..2d4ae262 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -23,7 +23,7 @@ {{ board.url }} - {{ meta_subject }} - + {{ boardlist.top }} {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} @@ -44,8 +44,7 @@ {% include 'attention_bar.html' %} - + {{ config.ad.top }} @@ -53,9 +52,6 @@ {% if config.global_message %}
{{ config.global_message }}
{% endif %}
- [{% trans %}Return{% endtrans %}] - [{% trans %}Go to bottom{% endtrans %}] -
{% if mod %}{% endif %} From 33ef3f9b013f9848a39df6aaa2ed092a3478fd11 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 14:14:22 +0200 Subject: [PATCH 103/212] synchronize catalog_link --- inc/config.php | 4 ++-- templates/thread.html | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/inc/config.php b/inc/config.php index 21f7b5cc..a3087162 100644 --- a/inc/config.php +++ b/inc/config.php @@ -931,8 +931,8 @@ // Show page navigation links at the top as well. $config['page_nav_top'] = false; - // Show "Catalog" link in page navigation. Use with the Catalog theme. - // $config['catalog_link'] = 'catalog.html'; + // Show "Catalog" link in page navigation. Use with the Catalog theme. Set to false to disable. + $config['catalog_link'] = 'catalog.html'; // Board categories. Only used in the "Categories" theme. // $config['categories'] = array( diff --git a/templates/thread.html b/templates/thread.html index 2d4ae262..3ddb5b1f 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -62,7 +62,9 @@ [{% trans %}Return{% endtrans %}] [{% trans %}Go to top{% endtrans %}] - [{% trans %}Catalog{% endtrans %}] + {% if config.catalog_link %} + [{% trans %}Catalog{% endtrans %}] + {% endif %} From d069a4c9fd9efb995fffa1d705dd201c09b7d13e Mon Sep 17 00:00:00 2001 From: Forkless Date: Mon, 3 Nov 2014 02:28:04 -0600 Subject: [PATCH 104/212] Added option for hiding IDs. --- js/forced-anon.js | 72 +++++++++++++++++++++++------------ stylesheets/style.css | 8 ++++ templates/post/poster_id.html | 4 +- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/js/forced-anon.js b/js/forced-anon.js index 11327ec9..26d0cb4f 100644 --- a/js/forced-anon.js +++ b/js/forced-anon.js @@ -18,10 +18,10 @@ if (active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || (window.Options && Options.get_tab('general'))) $(document).ready(function() { var force_anon = function() { - if($(this).children('a.capcode').length == 0) { + if ($(this).children('a.capcode').length == 0) { var id = $(this).parent().children('a.post_no:eq(1)').text(); - if($(this).children('a.email').length != 0) + if ($(this).children('a.email').length != 0) var p = $(this).children('a.email'); else var p = $(this); @@ -29,7 +29,7 @@ $(document).ready(function() { old_info[id] = {'name': p.children('span.name').text(), 'trip': p.children('span.trip').text()}; p.children('span.name').text('Anonymous'); - if(p.children('span.trip').length != 0) + if (p.children('span.trip').length != 0) p.children('span.trip').text(''); } }; @@ -40,44 +40,60 @@ $(document).ready(function() { var disable_fa = function() { $('p.intro label').each(function() { - if($(this).children('a.capcode').length == 0) { + if ($(this).children('a.capcode').length == 0) { var id = $(this).parent().children('a.post_no:eq(1)').text(); if(old_info[id]) { - if($(this).children('a.email').length != 0) + if ($(this).children('a.email').length != 0) var p = $(this).children('a.email'); else var p = $(this); p.children('span.name').text(old_info[id]['name']); - if(p.children('span.trip').length != 0) + if (p.children('span.trip').length != 0) p.children('span.trip').text(old_info[id]['trip']); } } }); }; + var toggle_id = function() { + if (localStorage.hideids == 'true'){ + $(this).addClass('hidden'); + } else { + $(this).removeClass('hidden'); + } + }; + old_info = {}; forced_anon = localStorage['forcedanon'] ? true : false; - var selector, event; - if (window.Options && Options.get_tab('general')) { - selector = '#forced-anon'; - event = 'change'; - Options.extend_tab("general", ""); - } - else { - selector = '#forced-anon'; - event = 'click'; + if (window.Options && Options.get_tab('general')) { + var s1 = '#hide-ids', s2 = '#forced-anon', e = 'change'; + Options.extend_tab("general", ""); + Options.extend_tab("general", ""); + } + else { + var s1 = '#hide-ids', s2 = '#forced-anon', e = 'click'; + $('hr:first').before(''); $('hr:first').before(''); $('div#forced-anon a').text(_('Forced anonymity')+' (' + (forced_anon ? _('enabled') : _('disabled')) + ')'); - } + } + $(s1).on(e, function(e) { + if (!localStorage.hideids || localStorage.hideids == 'false') { + localStorage.hideids = 'true'; + if (window.Options && Options.get_tab('general')) e.target.checked = true; + } else { + localStorage.hideids = 'false'; + if (window.Options && Options.get_tab('general')) e.target.checked = false; + } + $('.poster_id').each(toggle_id); + }); - $(selector).on(event, function() { + $(s2).on(e, function() { forced_anon = !forced_anon; - - if(forced_anon) { + if (forced_anon) { $('div#forced-anon a').text(_('Forced anonymity')+' ('+_('enabled')+')'); localStorage.forcedanon = true; enable_fa(); @@ -86,21 +102,27 @@ $(document).ready(function() { delete localStorage.forcedanon; disable_fa(); } - return false; }); + // initial option setup on script load + if (localStorage.hideids == 'true'){ + if (window.Options && Options.get_tab('general')) $('#hide-ids>input').prop('checked',true); + $('.poster_id').each(toggle_id); + } + if(forced_anon) { enable_fa(); - - if (window.Options && Options.get_tab('general')) { - $('#toggle-locked-threads>input').prop('checked', true); - } + if (window.Options && Options.get_tab('general')) { + $('#toggle-locked-threads>input').prop('checked', true); + } } $(document).on('new_post', function(e, post) { - if(forced_anon) + if (forced_anon) $(post).find('p.intro label').each(force_anon); + if (localStorage.hideids == 'true') + $(post).find('.poster_id').each(toggle_id); }); }); diff --git a/stylesheets/style.css b/stylesheets/style.css index bb7c2634..e54faf93 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -8,6 +8,10 @@ body { padding-right: 4px; } +.hidden { + display:none; +} + a,a:visited { text-decoration: underline; color: #34345C; @@ -905,6 +909,10 @@ pre { cursor: pointer; } +.poster_id::before { + content: " ID: "; +} + pre { /* Better code tags */ max-width:inherit; diff --git a/templates/post/poster_id.html b/templates/post/poster_id.html index 4efc56ff..51f0624f 100644 --- a/templates/post/poster_id.html +++ b/templates/post/poster_id.html @@ -1,7 +1,7 @@ {% if config.poster_ids %} {% if post.thread %} - ID: {{ post.ip|poster_id(post.thread) }} + {{ post.ip|poster_id(post.thread) }} {% else %} - ID: {{ post.ip|poster_id(post.id) }} + {{ post.ip|poster_id(post.id) }} {% endif %} {% endif %} From 126ee42b9dffe262457177c5dbb7ffa2db170763 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 14:34:42 +0200 Subject: [PATCH 105/212] better rules for stripping combined chars, based on 45c0d327619 by @ctrlcctrlv --- inc/functions.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 85b61b2e..d28d018d 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2152,16 +2152,7 @@ function strip_combining_chars($str) { $o = 0; $ord = ordutf8($char, $o); - if ($ord >= 768 && $ord <= 879) - continue; - - if ($ord >= 7616 && $ord <= 7679) - continue; - - if ($ord >= 8400 && $ord <= 8447) - continue; - - if ($ord >= 65056 && $ord <= 65071) + if ( ($ord >= 768 && $ord <= 879) || ($ord >= 1536 && $ord <= 1791) || ($ord >= 3655 && $ord <= 3659) || ($ord >= 7616 && $ord <= 7679) || ($ord >= 8400 && $ord <= 8447) || ($ord >= 65056 && $ord <= 65071)) continue; $str .= $char; From aa98ca337e327fb1d8e77e124bf49922190246c4 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 14:36:14 +0200 Subject: [PATCH 106/212] i think this lump of code deserves a version bump; v5.1.0 here --- install.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index 3aa8f56f..c5bbdf36 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); From 3515fdabe713fc9143a833d0f6a889ce150142aa Mon Sep 17 00:00:00 2001 From: sourcerect Date: Thu, 4 Jun 2015 04:19:04 -0400 Subject: [PATCH 107/212] Fix tab freeze when inlining Fixes ctrlcctrlv/infinity#451 --- js/inline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/inline.js b/js/inline.js index 686a540a..e581b79d 100644 --- a/js/inline.js +++ b/js/inline.js @@ -139,7 +139,7 @@ $(document).ready(function() { if (App.options.get('useInlining')) { var assign_inline = function() { - $('.body a[href*="'+location.pathname+'"]:not([rel]):not(.toolong a), .mentioned a') + $('.body a[href*="'+location.pathname+'"]').not('[rel]').not('.toolong > a').add('.mentioned a') .attr('onclick', null)// XXX disable highlightReply .off('click') .click(inline) From 65ea7b78c50ab3da60a541237f73b13cd2e7e6d8 Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sun, 10 May 2015 12:51:35 +0800 Subject: [PATCH 108/212] Catalog: click to scroll thread --- js/catalog.js | 10 ++++++++++ stylesheets/style.css | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/js/catalog.js b/js/catalog.js index 065fba7b..9adaeaef 100644 --- a/js/catalog.js +++ b/js/catalog.js @@ -35,4 +35,14 @@ if (active_page == 'catalog') $(function(){ if (catalog.image_size !== undefined) { $('#image_size').val(catalog.image_size).trigger('change'); } + + $('div.thread').on('click', function(e) { + if ($(this).css('overflow-y') === 'hidden') { + $(this).css('overflow-y', 'auto'); + $(this).css('width', '100%'); + } else { + $(this).css('overflow-y', 'hidden'); + $(this).css('width', 'auto'); + } + }); }); diff --git a/stylesheets/style.css b/stylesheets/style.css index e54faf93..b36a9de9 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -1033,7 +1033,6 @@ div.boardlist a { #watchlist-toggle, .watchThread, .watchlist-remove, #clearList, #clearGhosts { cursor: pointer; } -<<<<<<< HEAD #youtube-size input { width: 50px; From 8496b021a959af16682f8e846737020c53848bca Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 14:57:07 +0200 Subject: [PATCH 109/212] comment out global reports --- js/fix-report-delete-submit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/fix-report-delete-submit.js b/js/fix-report-delete-submit.js index 2bf7a70b..abed4d06 100644 --- a/js/fix-report-delete-submit.js +++ b/js/fix-report-delete-submit.js @@ -36,7 +36,7 @@ if ($('#delete-fields #password').length) { } Menu.add_item("report_menu", _("Report")); -Menu.add_item("global_report_menu", _("Global report")); +//Menu.add_item("global_report_menu", _("Global report")); Menu.onclick(function(e, $buf) { var ele = e.target.parentElement.parentElement; var $ele = $(ele); From 6e33de568d21e87f64bc7b890b9438b061aa15a9 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 15:03:53 +0200 Subject: [PATCH 110/212] hide-threads.js: add div.file to fields to hide; thanks fpdl --- js/hide-threads.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/hide-threads.js b/js/hide-threads.js index 175018ed..47417d39 100644 --- a/js/hide-threads.js +++ b/js/hide-threads.js @@ -36,7 +36,7 @@ $(document).ready(function(){ } } - var fields_to_hide = 'div.post,div.video-container,video,iframe,img:not(.unanimated),canvas,p.fileinfo,a.hide-thread-link,div.new-posts,br'; + var fields_to_hide = 'div.file,div.post,div.video-container,video,iframe,img:not(.unanimated),canvas,p.fileinfo,a.hide-thread-link,div.new-posts,br'; var do_hide_threads = function() { var id = $(this).children('p.intro').children('a.post_no:eq(1)').text(); From 2fa37278db9ad0e802a2b4c6a54c5d179ce6df35 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 15:09:25 +0200 Subject: [PATCH 111/212] boardlist goes before #top --- templates/thread.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/thread.html b/templates/thread.html index 3ddb5b1f..88e6ec6d 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -24,8 +24,8 @@ {{ board.url }} - {{ meta_subject }} - {{ boardlist.top }} + {% if pm %}
You have an unread PM{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.

{% endif %} {% if config.url_banner %}{% endif %}
From b476b660073adbe827836a7e7860b0fb3a061ff0 Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sun, 23 Aug 2015 09:04:37 +0800 Subject: [PATCH 112/212] [BUG] Image reject repost board option now also affects YT embeds --- inc/functions.php | 47 +++++++++++++++++++++++++++++++++++++++++++++++ post.php | 28 +++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index d28d018d..cbe0bc62 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2438,6 +2438,53 @@ function getPostByHashInThread($hash, $thread) { return false; } +function getPostByEmbed($embed) { + global $board, $config; + $matches = array(); + foreach ($config['embedding'] as &$e) { + if (preg_match($e[0], $embed, $matches) && isset($matches[1]) && !empty($matches[1])) { + $embed = '%'.$matches[1].'%'; + break; + } + } + + if (!isset($embed)) return false; + + $query = prepare(sprintf("SELECT `id`,`thread` FROM ``posts_%s`` WHERE `embed` LIKE :embed", $board['uri'])); + $query->bindValue(':embed', $embed, PDO::PARAM_STR); + $query->execute() or error(db_error($query)); + + if ($post = $query->fetch(PDO::FETCH_ASSOC)) { + return $post; + } + + return false; +} + +function getPostByEmbedInThread($embed, $thread) { + global $board, $config; + $matches = array(); + foreach ($config['embedding'] as &$e) { + if (preg_match($e[0], $embed, $matches) && isset($matches[1]) && !empty($matches[1])) { + $embed = '%'.$matches[1].'%'; + break; + } + } + + if (!isset($embed)) return false; + + $query = prepare(sprintf("SELECT `id`,`thread` FROM ``posts_%s`` WHERE `embed` = :embed AND ( `thread` = :thread OR `id` = :thread )", $board['uri'])); + $query->bindValue(':embed', $embed, PDO::PARAM_STR); + $query->bindValue(':thread', $thread, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if ($post = $query->fetch(PDO::FETCH_ASSOC)) { + return $post; + } + + return false; +} + function undoImage(array $post) { if (!$post['has_file'] || !isset($post['files'])) return; diff --git a/post.php b/post.php index 3b4f3311..06a9dc74 100644 --- a/post.php +++ b/post.php @@ -294,6 +294,32 @@ if (isset($_POST['delete'])) { if (!isset($post['embed'])) { error($config['error']['invalid_embed']); } + + if ($config['image_reject_repost']) { + if ($p = getPostByEmbed($post['embed'])) { + error(sprintf($config['error']['fileexists'], + ($post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root']) . + ($board['dir'] . $config['dir']['res'] . + ($p['thread'] ? + $p['thread'] . '.html#' . $p['id'] + : + $p['id'] . '.html' + )) + )); + } + } else if (!$post['op'] && $config['image_reject_repost_in_thread']) { + if ($p = getPostByEmbedInThread($post['embed'], $post['thread'])) { + error(sprintf($config['error']['fileexistsinthread'], + ($post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root']) . + ($board['dir'] . $config['dir']['res'] . + ($p['thread'] ? + $p['thread'] . '.html#' . $p['id'] + : + $p['id'] . '.html' + )) + )); + } + } } if (!hasPermission($config['mod']['bypass_field_disable'], $board['uri'])) { @@ -798,7 +824,7 @@ if (isset($_POST['delete'])) { )); } } - } + } if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) { undoImage($post); From 7831da83fc09ff90a0f99641bde5e2d8c08e84c5 Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 16 Feb 2015 16:21:30 -0800 Subject: [PATCH 113/212] New event: rebuildpost, allows you to bind events to ?/edit --- inc/functions.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index cbe0bc62..4746b034 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1166,19 +1166,22 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) { // rebuild post (markup) function rebuildPost($id) { - global $board; + global $board, $mod; - $query = prepare(sprintf("SELECT `body_nomarkup`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); + $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); if ((!$post = $query->fetch(PDO::FETCH_ASSOC)) || !$post['body_nomarkup']) return false; - markup($body = &$post['body_nomarkup']); + markup($post['body'] = &$post['body_nomarkup']); + $post = (object)$post; + event('rebuildpost', $post); + $post = (array)$post; $query = prepare(sprintf("UPDATE ``posts_%s`` SET `body` = :body WHERE `id` = :id", $board['uri'])); - $query->bindValue(':body', $body); + $query->bindValue(':body', $post['body']); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); From ce3ce4f1b65198083c232897c169313640f23038 Mon Sep 17 00:00:00 2001 From: 8chan Date: Mon, 16 Feb 2015 16:24:54 -0800 Subject: [PATCH 114/212] Fix *0 secure tripcodes caused by accidentally feeding + signs to crypt() --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 4746b034..6a34e7a8 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2374,7 +2374,7 @@ function generate_tripcode($name) { if (isset($config['custom_tripcode']["##{$trip}"])) $trip = $config['custom_tripcode']["##{$trip}"]; else - $trip = '!!' . substr(crypt($trip, '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4)), -10); + $trip = '!!' . substr(crypt($trip, str_replace('+', '.', '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4))), -10); } else { if (isset($config['custom_tripcode']["#{$trip}"])) $trip = $config['custom_tripcode']["#{$trip}"]; From 6dd1420f91410531bb2dac506f821d959bf74d0a Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 18 Feb 2015 16:55:03 -0800 Subject: [PATCH 115/212] Add event to quote backlinks --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 6a34e7a8..8f650828 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1952,7 +1952,7 @@ function markup(&$body, $track_cites = false, $op = false) { } if (isset($cited_posts[$cite])) { - $replacement = '' . '>>' . $cite . @@ -2051,7 +2051,7 @@ function markup(&$body, $track_cites = false, $op = false) { $replacement = '' . '>>>/' . $_board . '/' . $cite . ''; From 7911c374e86ef8fa69c4545867b9d9d3de12d5c6 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 25 Feb 2015 17:21:49 -0800 Subject: [PATCH 116/212] Public action logs commit (log.php) Note: In a previous commit, I began making inc/mod/auth.php more modular with the check_login() function. Including it does NOT check mod login by default anymore like it does on vichan. You have to call check_login(). I've finally included it in inc/functions.php. If you have any custom pages that use inc/mod/auth.php, just including functions.php is enough now. =================================== Also: backports 351375185e5 (early 404) --- inc/config.php | 22 +++++++++++++++++----- inc/functions.php | 22 ++++++++++++++++++++-- inc/mod/auth.php | 2 +- inc/mod/pages.php | 36 ++++++++++++++++++++++++++++++++++++ install.sql | 1 + log.php | 24 ++++++++++++++++++++++++ mod.php | 6 +++--- post.php | 5 +++-- templates/mod/log.html | 16 ++++++++++++++-- tools/inc/cli.php | 1 - 10 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 log.php diff --git a/inc/config.php b/inc/config.php index a3087162..b6bc3b69 100644 --- a/inc/config.php +++ b/inc/config.php @@ -511,6 +511,13 @@ // The timeout for the above, in seconds. $config['upload_by_url_timeout'] = 15; + // Enable early 404? With default settings, a thread would 404 if it was to leave page 3, if it had less + // than 3 replies. + $config['early_404'] = false; + + $config['early_404_page'] = 3; + $config['early_404_replies'] = 5; + // A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts // as they are submitted and changes or censors particular words or phrases. @@ -1537,25 +1544,30 @@ /* * ==================== - * Public post search + * Public pages * ==================== */ + + // Public post search settings $config['search'] = array(); // Enable the search form $config['search']['enable'] = false; // Maximal number of queries per IP address per minutes - $config['search']['queries_per_minutes'] = Array(15, 2); + $config['search']['queries_per_minutes'] = Array(15, 2); // Global maximal number of queries per minutes - $config['search']['queries_per_minutes_all'] = Array(50, 2); + $config['search']['queries_per_minutes_all'] = Array(50, 2); // Limit of search results - $config['search']['search_limit'] = 100; + $config['search']['search_limit'] = 100; // Boards for searching - //$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e'); + //$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e'); + + // Enable public logs? 0: NO, 1: YES, 2: YES, but drop names + $config['public_logs'] = 0; /* * ==================== diff --git a/inc/functions.php b/inc/functions.php index 8f650828..378e40b3 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -18,6 +18,7 @@ require_once 'inc/template.php'; require_once 'inc/database.php'; require_once 'inc/events.php'; require_once 'inc/api.php'; +require_once 'inc/mod/auth.php'; require_once 'inc/polyfill.php'; if (!extension_loaded('gettext')) { @@ -524,7 +525,8 @@ function setupBoard($array) { $board = array( 'uri' => $array['uri'], 'title' => $array['title'], - 'subtitle' => $array['subtitle'] + 'subtitle' => $array['subtitle'], + #'indexed' => $array['indexed'], ); // older versions @@ -1270,7 +1272,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { return true; } -function clean() { +function clean($pid = false) { global $board, $config; $offset = round($config['max_pages']*$config['threads_per_page']); @@ -1281,6 +1283,22 @@ function clean() { $query->execute() or error(db_error($query)); while ($post = $query->fetch(PDO::FETCH_ASSOC)) { deletePost($post['id'], false, false); + if ($pid) modLog("Automatically deleting thread #{$post['id']} due to new thread #{$pid}"); + } + + // Bump off threads with X replies earlier, spam prevention method + if ($config['early_404']) { + $offset = round($config['early_404_page']*$config['threads_per_page']); + $query = prepare(sprintf("SELECT `id` AS `thread_id`, (SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `thread` = `thread_id`) AS `reply_count` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001", $board['uri'], $board['uri'])); + $query->bindValue(':offset', $offset, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + if ($post['reply_count'] < $config['early_404_replies']) { + deletePost($post['thread_id'], false, false); + if ($pid) modLog("Automatically deleting thread #{$post['thread_id']} due to new thread #{$pid} (early 404 is set, #{$post['thread_id']} had {$post['reply_count']} replies)"); + } + } } } diff --git a/inc/mod/auth.php b/inc/mod/auth.php index 009e93ad..42f34196 100644 --- a/inc/mod/auth.php +++ b/inc/mod/auth.php @@ -130,7 +130,7 @@ function destroyCookies() { function modLog($action, $_board=null) { global $mod, $board, $config; $query = prepare("INSERT INTO ``modlogs`` VALUES (:id, :ip, :board, :time, :text)"); - $query->bindValue(':id', $mod['id'], PDO::PARAM_INT); + $query->bindValue(':id', (isset($mod['id']) ? $mod['id'] : -1), PDO::PARAM_INT); $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); $query->bindValue(':time', time(), PDO::PARAM_INT); $query->bindValue(':text', $action); diff --git a/inc/mod/pages.php b/inc/mod/pages.php index a90fbbd6..ca12eaf1 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -698,6 +698,42 @@ function mod_user_log($username, $page_no = 1) { mod_page(_('Moderation log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'username' => $username)); } +function mod_board_log($board, $page_no = 1, $hide_names = false, $public = false) { + global $config; + + if ($page_no < 1) + error($config['error']['404']); + + if (!hasPermission($config['mod']['mod_board_log'], $board) && !$public) + error($config['error']['noaccess']); + + $query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board ORDER BY `time` DESC LIMIT :offset, :limit"); + $query->bindValue(':board', $board); + $query->bindValue(':limit', $config['mod']['modlog_page'], PDO::PARAM_INT); + $query->bindValue(':offset', ($page_no - 1) * $config['mod']['modlog_page'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + $logs = $query->fetchAll(PDO::FETCH_ASSOC); + + if (empty($logs) && $page_no > 1) + error($config['error']['404']); + + if (!hasPermission($config['mod']['show_ip'])) { + // Supports ipv4 only! + foreach ($logs as $i => &$log) { + $log['text'] = preg_replace_callback('/(?:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:<\/a>)?/', function($matches) { + return "xxxx";//less_ip($matches[1]); + }, $log['text']); + } + } + + $query = prepare("SELECT COUNT(*) FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board"); + $query->bindValue(':board', $board); + $query->execute() or error(db_error($query)); + $count = $query->fetchColumn(); + + mod_page(_('Board log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'board' => $board, 'hide_names' => $hide_names, 'public' => $public)); +} + function mod_view_board($boardName, $page_no = 1) { global $config, $mod; diff --git a/install.sql b/install.sql index 7e661450..024cb349 100644 --- a/install.sql +++ b/install.sql @@ -65,6 +65,7 @@ CREATE TABLE IF NOT EXISTS `boards` ( `uri` varchar(58) CHARACTER SET utf8 NOT NULL, `title` tinytext NOT NULL, `subtitle` tinytext, + -- `indexed` boolean default true, PRIMARY KEY (`uri`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; diff --git a/log.php b/log.php new file mode 100644 index 00000000..1a660c4c --- /dev/null +++ b/log.php @@ -0,0 +1,24 @@ + {% if log.username %} - {{ log.username|e }} + {% if hide_names %} + hidden + {% else %} + {% if not mod|hasPermission(config.mod.modlog) %} + {{ log.username|e }} + {% else %} + {{ log.username|e }} + {% endif %} + {% endif %} {% elseif log.mod == -1 %} system {% else %} @@ -44,7 +52,11 @@ {% if count > logs|count %}

{% for i in range(0, (count - 1) / config.mod.modlog_page) %} - [{{ i + 1 }}] + {% if public %} + [{{ i + 1 }}] + {% else %} + [{{ i + 1 }}] + {% endif %} {% endfor %}

{% endif %} diff --git a/tools/inc/cli.php b/tools/inc/cli.php index f3e8824f..95d51573 100644 --- a/tools/inc/cli.php +++ b/tools/inc/cli.php @@ -39,7 +39,6 @@ if(!getenv('TINYBOARD_PATH')) { putenv('TINYBOARD_PATH=' . getcwd()); require 'inc/functions.php'; -require 'inc/mod/auth.php'; $mod = Array( 'id' => -1, From 95b1e103cb9f349d7d82dca071636d5cde855ead Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sun, 29 Mar 2015 09:18:14 +0800 Subject: [PATCH 117/212] Edit static pages commit --- .gitmodules | 6 ++ inc/config.php | 6 ++ inc/functions.php | 43 ++++++++++ inc/mod/pages.php | 162 +++++++++++++++++++++++++++++++++++ install.sql | 20 ++++- mod.php | 18 ++-- templates/mod/edit_page.html | 29 +++++++ templates/mod/pages.html | 34 ++++++++ tools/import_rules.php | 16 ++++ 9 files changed, 328 insertions(+), 6 deletions(-) create mode 100644 templates/mod/edit_page.html create mode 100644 templates/mod/pages.html create mode 100644 tools/import_rules.php diff --git a/.gitmodules b/.gitmodules index 73f90e0a..df07fdf3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "js/wPaint"] path = js/wPaint url = https://github.com/vichan-devel/wPaint.git + branch = master + +[submodule "inc/lib/parsedown"] + path = inc/lib/parsedown + url = https://github.com/vichan-devel/parsedown + branch = master diff --git a/inc/config.php b/inc/config.php index b6bc3b69..3fdb8d2f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1498,6 +1498,9 @@ $config['mod']['ban_appeals'] = MOD; // View the recent posts page $config['mod']['recent'] = MOD; + // Create pages + $config['mod']['edit_pages'] = MOD; + $config['pages_max'] = 10; // Config editor permissions $config['mod']['config'] = array(); @@ -1702,3 +1705,6 @@ // Use CAPTCHA for reports? $config['report_captcha'] = false; + + // Allowed HTML tags in ?/edit_pages. + $config['allowed_html'] = 'a[href|title],p,br,li,ol,ul,strong,em,u,h2,b,i,tt,div,img[src|alt|title],hr'; diff --git a/inc/functions.php b/inc/functions.php index 378e40b3..7970d05a 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -20,6 +20,7 @@ require_once 'inc/events.php'; require_once 'inc/api.php'; require_once 'inc/mod/auth.php'; require_once 'inc/polyfill.php'; +//require_once 'inc/lib/parsedown/Parsedown.php'; // we don't need that right now, do we? if (!extension_loaded('gettext')) { require_once 'inc/lib/gettext/gettext.inc'; @@ -2739,3 +2740,45 @@ function link_for($post, $page50 = false, $foreignlink = false, $thread = false) return sprintf($tpl, $id, $slug); } + +function prettify_textarea($s){ + return str_replace("\t", ' ', str_replace("\n", ' ', htmlentities($s))); +} + +class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter { + public $name = 'NoExternalImages'; + public function filter(&$uri, $c, $context) { + global $config; + $ct = $context->get('CurrentToken'); + + if (!$ct || $ct->name !== 'img') return true; + + if (!isset($uri->host) && !isset($uri->scheme)) return true; + + if (!in_array($uri->scheme . '://' . $uri->host . '/', $config['allowed_offsite_urls'])) { + error('No off-site links in board announcement images.'); + } + + return true; + } +} + +function purify_html($s) { + global $config; + + $c = HTMLPurifier_Config::createDefault(); + $c->set('HTML.Allowed', $config['allowed_html']); + $uri = $c->getDefinition('URI'); + $uri->addFilter(new HTMLPurifier_URIFilter_NoExternalImages(), $c); + $purifier = new HTMLPurifier($c); + $clean_html = $purifier->purify($s); + return $clean_html; +} + +function markdown($s) { + $pd = new Parsedown(); + $pd->setMarkupEscaped(true); + $pd->setimagesEnabled(false); + + return $pd->text($s); +} diff --git a/inc/mod/pages.php b/inc/mod/pages.php index ca12eaf1..328380d8 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -2628,6 +2628,167 @@ function mod_theme_rebuild($theme_name) { )); } +// This needs to be done for `secure` CSRF prevention compatibility, otherwise the $board will be read in as the token if editing global pages. +function delete_page_base($page = '', $board = false) { + global $config, $mod; + + if (empty($board)) + $board = false; + + if (!$board && $mod['boards'][0] !== '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_pages'], $board)) + error($config['error']['noaccess']); + + if ($board !== FALSE && !openBoard($board)) + error($config['error']['noboard']); + + if ($board) { + $query = prepare('DELETE FROM ``pages`` WHERE `board` = :board AND `name` = :name'); + $query->bindValue(':board', ($board ? $board : NULL)); + } else { + $query = prepare('DELETE FROM ``pages`` WHERE `board` IS NULL AND `name` = :name'); + } + $query->bindValue(':name', $page); + $query->execute() or error(db_error($query)); + + header('Location: ?/edit_pages' . ($board ? ('/' . $board) : ''), true, $config['redirect_http']); +} + +function mod_delete_page($page = '') { + delete_page_base($page); +} + +function mod_delete_page_board($page = '', $board = false) { + delete_page_base($page, $board); +} + +function mod_edit_page($id) { + global $config, $mod, $board; + + $query = prepare('SELECT * FROM ``pages`` WHERE `id` = :id'); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + $page = $query->fetch(); + + if (!$page) + error(_('Could not find the page you are trying to edit.')); + + if (!$page['board'] && $mod['boards'][0] !== '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_pages'], $page['board'])) + error($config['error']['noaccess']); + + if ($page['board'] && !openBoard($page['board'])) + error($config['error']['noboard']); + + if (isset($_POST['method'], $_POST['content'])) { + $content = $_POST['content']; + $method = $_POST['method']; + $page['type'] = $method; + + if (!in_array($method, array('markdown', 'html', 'infinity'))) + error(_('Unrecognized page markup method.')); + + switch ($method) { + case 'markdown': + $write = markdown($content); + break; + case 'html': + if (hasPermission($config['mod']['rawhtml'])) { + $write = $content; + } else { + $write = purify_html($content); + } + break; + case 'infinity': + $c = $content; + markup($content); + $write = $content; + $content = $c; + } + + if (!isset($write) or !$write) + error(_('Failed to mark up your input for some reason...')); + + $query = prepare('UPDATE ``pages`` SET `type` = :method, `content` = :content WHERE `id` = :id'); + $query->bindValue(':method', $method); + $query->bindValue(':content', $content); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + + $fn = ($board['uri'] ? ($board['uri'] . '/') : '') . $page['name'] . '.html'; + $body = "
$write
"; + $html = Element('page.html', array('config' => $config, 'body' => $body, 'title' => utf8tohtml($page['title']))); + file_write($fn, $html); + } + + if (!isset($content)) { + $query = prepare('SELECT `content` FROM ``pages`` WHERE `id` = :id'); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + $content = $query->fetchColumn(); + } + + mod_page(sprintf(_('Editing static page: %s'), $page['name']), 'mod/edit_page.html', array('page' => $page, 'token' => make_secure_link_token("edit_page/$id"), 'content' => prettify_textarea($content), 'board' => $board)); +} + +function mod_pages($board = false) { + global $config, $mod, $pdo; + + if (empty($board)) + $board = false; + + if (!$board && $mod['boards'][0] !== '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_pages'], $board)) + error($config['error']['noaccess']); + + if ($board !== FALSE && !openBoard($board)) + error($config['error']['noboard']); + + if ($board) { + $query = prepare('SELECT * FROM ``pages`` WHERE `board` = :board'); + $query->bindValue(':board', $board); + } else { + $query = query('SELECT * FROM ``pages`` WHERE `board` IS NULL'); + } + $query->execute() or error(db_error($query)); + $pages = $query->fetchAll(PDO::FETCH_ASSOC); + + if (isset($_POST['page'])) { + if ($board and sizeof($pages) > $config['pages_max']) + error(sprintf(_('Sorry, this site only allows %d pages per board.'), $config['pages_max'])); + + if (!preg_match('/^[a-z0-9]{1,255}$/', $_POST['page'])) + error(_('Page names must be < 255 chars and may only contain lowercase letters A-Z and digits 1-9.')); + + foreach ($pages as $i => $p) { + if ($_POST['page'] === $p['name']) + error(_('Refusing to create a new page with the same name as an existing one.')); + } + + $title = ($_POST['title'] ? $_POST['title'] : NULL); + + $query = prepare('INSERT INTO ``pages``(board, title, name) VALUES(:board, :title, :name)'); + $query->bindValue(':board', ($board ? $board : NULL)); + $query->bindValue(':title', $title); + $query->bindValue(':name', $_POST['page']); + $query->execute() or error(db_error($query)); + + $pages[] = array('id' => $pdo->lastInsertId(), 'name' => $_POST['page'], 'board' => $board, 'title' => $title); + } + + foreach ($pages as $i => &$p) { + $p['delete_token'] = make_secure_link_token('edit_pages/delete/' . $p['name'] . ($board ? ('/' . $board) : '')); + } + + mod_page(_('Pages'), 'mod/pages.html', array('pages' => $pages, 'token' => make_secure_link_token('edit_pages' . ($board ? ('/' . $board) : '')), 'board' => $board)); +} + function mod_debug_antispam() { global $pdo, $config; @@ -2744,3 +2905,4 @@ function mod_debug_apc() { mod_page(_('Debug: APC'), 'mod/debug/apc.html', array('cached_vars' => $cached_vars)); } + diff --git a/install.sql b/install.sql index 024cb349..720e4ed5 100644 --- a/install.sql +++ b/install.sql @@ -245,7 +245,7 @@ CREATE TABLE IF NOT EXISTS `search_queries` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -- -------------------------------------------------------- @@ -297,6 +297,24 @@ CREATE TABLE IF NOT EXISTS `ban_appeals` ( KEY `ban_id` (`ban_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; +-- -------------------------------------------------------- + +-- +-- Table structure for table `pages` +-- + +CREATE TABLE `pages` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `board` varchar(255) DEFAULT NULL, + `name` varchar(255) NOT NULL, + `title` varchar(255) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + `content` text, + PRIMARY KEY (`id`), + UNIQUE KEY `u_pages` (`name`,`board`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + +>>>>>>> 12fa8ec... Edit static pages commit /*!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/mod.php b/mod.php index a6ff00cf..fbe679ac 100644 --- a/mod.php +++ b/mod.php @@ -33,11 +33,19 @@ $pages = array( '/log' => 'log', // modlog '/log/(\d+)' => 'log', // modlog - '/log:([^/]+)' => 'user_log', // modlog - '/log:([^/]+)/(\d+)' => 'user_log', // modlog - '/news' => 'secure_POST news', // view news - '/news/(\d+)' => 'secure_POST news', // view news - '/news/delete/(\d+)' => 'secure news_delete', // delete from news + '/log:([^/:]+)' => 'user_log', // modlog + '/log:([^/:]+)/(\d+)' => 'user_log', // modlog + '/log:b:([^/]+)' => 'board_log', // modlog + '/log:b:([^/]+)/(\d+)' => 'board_log', // modlog + + '/edit_news' => 'secure_POST news', // view news + '/edit_news/(\d+)' => 'secure_POST news', // view news + '/edit_news/delete/(\d+)' => 'secure news_delete', // delete from news + + '/edit_pages(?:/?(\%b)?)' => 'secure_POST pages', + '/edit_page/(\d+)' => 'secure_POST edit_page', + '/edit_pages/delete/([a-z0-9]+)' => 'secure delete_page', + '/edit_pages/delete/([a-z0-9]+)/(\%b)' => 'secure delete_page_board', '/noticeboard' => 'secure_POST noticeboard', // view noticeboard '/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard diff --git a/templates/mod/edit_page.html b/templates/mod/edit_page.html new file mode 100644 index 00000000..3d132767 --- /dev/null +++ b/templates/mod/edit_page.html @@ -0,0 +1,29 @@ +
+ + + + + + + +
{% trans %}Markup method{% endtrans %} + {% set allowed_html = config.allowed_html %} + {% trans %}

"markdown" is provided by parsedown. Note: images disabled.

+

"html" allows the following tags:
{{ allowed_html }}

+

"infinity" is the same as what is used in posts.

+

This page will not convert between formats,
choose it once or do the conversion yourself!

{% endtrans %} +
+ +
{% trans %}Page content{% endtrans %} +
+ {% trans %}Page will appear at:{% endtrans %} + {% if board %} {{ config.domain }}/{{ board.uri }}/{{ page.name }}.html + {% else %} {{ config.site }}/{{ page.name }}.html + {% endif %}
+ + +
diff --git a/templates/mod/pages.html b/templates/mod/pages.html new file mode 100644 index 00000000..c2395c02 --- /dev/null +++ b/templates/mod/pages.html @@ -0,0 +1,34 @@ + +
+

+{% if board %} +{% set page_max = config.pages_max %} +{% trans %}This page allows you to create static pages for your board. The limit is {{ page_max }} pages per board. You will still have to link to your pages somewhere in your board, for example in a sticky or in the board's announcement. To make links in the board's announcement, use <a> HTML tags.{% endtrans %} +{% else %} +{% trans %}This page allows you to create static pages for your imageboard.{% endtrans %} +{% endif %} +

{% trans %}Existing pages{% endtrans %}

+{% if pages %} +
+ + +{% for page in pages %} + +{% endfor %} +{% else %} +No pages yet! +{% endif %} +
{% trans %}URL{% endtrans %}{% trans %}Title{% endtrans %}{% trans %}Edit{% endtrans %}{% trans %}Delete{% endtrans %}
{{ page.name }}{{ page.title }}{% trans %}Edit{% endtrans %}{% trans %}Delete{% endtrans %}
+
+
+

{% trans %}Create a new page{% endtrans %}

+
+ + + + +
{% trans %}URL{% endtrans %}{% trans %}Title{% endtrans %}
+ +
+ +
diff --git a/tools/import_rules.php b/tools/import_rules.php new file mode 100644 index 00000000..f583f035 --- /dev/null +++ b/tools/import_rules.php @@ -0,0 +1,16 @@ + $b) { + $rules = @file_get_contents($b.'/rules.txt'); + if ($rules && !empty(trim($rules))) { + $query = prepare('INSERT INTO ``pages``(name, title, type, board, content) VALUES("rules", "Rules", "html", :board, :content)'); + $query->bindValue(':board', $b); + $query->bindValue(':content', $rules); + $query->execute() or error(db_error($query)); + } +} From d726eaf195a18a35c44fe0b08aa0960e4a79e272 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 16:07:21 +0200 Subject: [PATCH 118/212] we don't have a htmlpurifier yet ;_; --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 7970d05a..845c4cb3 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2745,7 +2745,7 @@ function prettify_textarea($s){ return str_replace("\t", ' ', str_replace("\n", ' ', htmlentities($s))); } -class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter { +/*class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter { public $name = 'NoExternalImages'; public function filter(&$uri, $c, $context) { global $config; @@ -2761,7 +2761,7 @@ class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter { return true; } -} +}*/ function purify_html($s) { global $config; From 91c02c3ec41e5c7fa28e70ec4c9b4194ec941620 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 16:21:30 +0200 Subject: [PATCH 119/212] board pages: add a migration --- install.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index c5bbdf36..5a6512ba 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); From d788131202604a32986c4ba2bfca841c844ff010 Mon Sep 17 00:00:00 2001 From: 8chan Date: Sun, 14 Dec 2014 03:27:26 -0800 Subject: [PATCH 120/212] Allow a board called news to exist --- inc/mod/pages.php | 8 ++++---- templates/mod/dashboard.html | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 328380d8..6d1e20b4 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -608,7 +608,7 @@ function mod_news($page_no = 1) { rebuildThemes('news'); - header('Location: ?/news#' . $pdo->lastInsertId(), true, $config['redirect_http']); + header('Location: ?/edit_news#' . $pdo->lastInsertId(), true, $config['redirect_http']); } $query = prepare("SELECT * FROM ``news`` ORDER BY `id` DESC LIMIT :offset, :limit"); @@ -621,14 +621,14 @@ function mod_news($page_no = 1) { error($config['error']['404']); foreach ($news as &$entry) { - $entry['delete_token'] = make_secure_link_token('news/delete/' . $entry['id']); + $entry['delete_token'] = make_secure_link_token('edit_news/delete/' . $entry['id']); } $query = prepare("SELECT COUNT(*) FROM ``news``"); $query->execute() or error(db_error($query)); $count = $query->fetchColumn(); - mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('news'))); + mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('edit_news'))); } function mod_news_delete($id) { @@ -643,7 +643,7 @@ function mod_news_delete($id) { modLog('Deleted a news entry'); - header('Location: ?/news', true, $config['redirect_http']); + header('Location: ?/edit_news', true, $config['redirect_http']); } function mod_log($page_no = 1) { diff --git a/templates/mod/dashboard.html b/templates/mod/dashboard.html index 7e25555b..5c44a522 100644 --- a/templates/mod/dashboard.html +++ b/templates/mod/dashboard.html @@ -62,7 +62,7 @@ {% endif %}
  • {% trans 'View all noticeboard entries' %}
  • {% endif %} -
  • {% trans 'News' %}
  • +
  • {% trans 'News' %}
  • {% trans 'PM inbox' %} From ab02a4272514410fc053ced9c17a26bc0c8f1cf4 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 16:27:43 +0200 Subject: [PATCH 121/212] maybe we can try to load Parsedown, after all we can silence the error --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 845c4cb3..e9692d56 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -20,7 +20,7 @@ require_once 'inc/events.php'; require_once 'inc/api.php'; require_once 'inc/mod/auth.php'; require_once 'inc/polyfill.php'; -//require_once 'inc/lib/parsedown/Parsedown.php'; // we don't need that right now, do we? +@include_once 'inc/lib/parsedown/Parsedown.php'; // fail silently, this isn't a critical piece of code if (!extension_loaded('gettext')) { require_once 'inc/lib/gettext/gettext.inc'; From 505adffcdca1c1141b7b46f862b7939f2f4acf45 Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Fri, 3 Apr 2015 14:56:28 +0800 Subject: [PATCH 122/212] =?UTF-8?q?Cyclical=20threads=20=E2=99=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inc/api.php | 1 + inc/config.php | 5 +++++ inc/functions.php | 8 +++++++- inc/mod/pages.php | 22 ++++++++++++++++++++++ install.php | 7 ++++++- mod.php | 1 + post.php | 11 ++++++++++- templates/post/post_controls.html | 8 +++++++- templates/post_thread.html | 13 ++++++++++--- templates/posts.sql | 1 + 10 files changed, 70 insertions(+), 7 deletions(-) diff --git a/inc/api.php b/inc/api.php index b2d8adec..b280c25b 100644 --- a/inc/api.php +++ b/inc/api.php @@ -32,6 +32,7 @@ class Api { 'images' => 'images', 'sticky' => 'sticky', 'locked' => 'locked', + 'cycle' => 'cyclical', 'bump' => 'last_modified', 'embed' => 'embed', ); diff --git a/inc/config.php b/inc/config.php index 3fdb8d2f..82fdc882 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1245,6 +1245,8 @@ $config['mod']['link_bumpunlock'] = '[-Sage]'; $config['mod']['link_editpost'] = '[Edit]'; $config['mod']['link_move'] = '[Move]'; + $config['mod']['link_cycle'] = '[Cycle]'; + $config['mod']['link_uncycle'] = '[-Cycle]'; // Moderator capcodes. $config['capcode'] = ' ## %s'; @@ -1388,6 +1390,9 @@ $config['mod']['deletebyip_global'] = ADMIN; // Sticky a thread $config['mod']['sticky'] = MOD; + // Cycle a thread + $config['mod']['cycle'] = MOD; + $config['cycle_limit'] = &$config['reply_limit']; // Lock a thread $config['mod']['lock'] = MOD; // Post in a locked thread diff --git a/inc/functions.php b/inc/functions.php index e9692d56..1bd1f32f 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1021,7 +1021,7 @@ function insertFloodPost(array $post) { function post(array $post) { global $pdo, $board; - $query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, 0, :embed, :slug)", $board['uri'])); + $query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, :cycle, 0, :embed, :slug)", $board['uri'])); // Basic stuff if (!empty($post['subject'])) { @@ -1061,6 +1061,12 @@ function post(array $post) { $query->bindValue(':locked', false, PDO::PARAM_INT); } + if ($post['op'] && $post['mod'] && isset($post['cycle']) && $post['cycle']) { + $query->bindValue(':cycle', true, PDO::PARAM_INT); + } else { + $query->bindValue(':cycle', false, PDO::PARAM_INT); + } + if ($post['mod'] && isset($post['capcode']) && $post['capcode']) { $query->bindValue(':capcode', $post['capcode'], PDO::PARAM_INT); } else { diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 6d1e20b4..303fa3dd 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1089,6 +1089,28 @@ function mod_sticky($board, $unsticky, $post) { header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']); } +function mod_cycle($board, $uncycle, $post) { + global $config; + + if (!openBoard($board)) + error($config['error']['noboard']); + + if (!hasPermission($config['mod']['cycle'], $board)) + error($config['error']['noaccess']); + + $query = prepare(sprintf('UPDATE ``posts_%s`` SET `cycle` = :cycle WHERE `id` = :id AND `thread` IS NULL', $board)); + $query->bindValue(':id', $post); + $query->bindValue(':cycle', $uncycle ? 0 : 1); + $query->execute() or error(db_error($query)); + if ($query->rowCount()) { + modLog(($uncycle ? 'Made not cyclical' : 'Made cyclical') . " thread #{$post}"); + buildThread($post); + buildIndex(); + } + + header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']); +} + function mod_bumplock($board, $unbumplock, $post) { global $config; diff --git a/install.php b/install.php index 5a6512ba..de4d3dd0 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); diff --git a/mod.php b/mod.php index fbe679ac..0378d55c 100644 --- a/mod.php +++ b/mod.php @@ -82,6 +82,7 @@ $pages = array( '/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address '/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread '/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread + '/(\%b)/(un)?cycle/(\d+)' => 'secure cycle', // cycle thread '/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread '/themes' => 'themes_list', // manage themes diff --git a/post.php b/post.php index 0d3292bb..98aefe05 100644 --- a/post.php +++ b/post.php @@ -265,7 +265,7 @@ if (isset($_POST['delete'])) { //Check if thread exists if (!$post['op']) { - $query = prepare(sprintf("SELECT `sticky`,`locked`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri'])); + $query = prepare(sprintf("SELECT `sticky`,`locked`,`cycle`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri'])); $query->bindValue(':id', $post['thread'], PDO::PARAM_INT); $query->execute() or error(db_error()); @@ -867,6 +867,15 @@ if (isset($_POST['delete'])) { $post['slug'] = slugify($post); insertFloodPost($post); + + // Handle cyclical threads + if (!$post['op'] && isset($thread['cycle']) && $thread['cycle']) { + // Query is a bit weird due to "This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'" (MariaDB Ver 15.1 Distrib 10.0.17-MariaDB, for Linux (x86_64)) + $query = prepare(sprintf('DELETE FROM ``posts_%s`` WHERE `thread` = :thread AND `id` NOT IN (SELECT `id` FROM (SELECT `id` FROM ``posts_%s`` WHERE `thread` = :thread ORDER BY `id` DESC LIMIT :limit) i)', $board['uri'], $board['uri'])); + $query->bindValue(':thread', $post['thread']); + $query->bindValue(':limit', $config['cycle_limit'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + } if (isset($post['antispam_hash'])) { incrementSpamHash($post['antispam_hash']); diff --git a/templates/post/post_controls.html b/templates/post/post_controls.html index 99a8484c..60dc6fc4 100644 --- a/templates/post/post_controls.html +++ b/templates/post/post_controls.html @@ -41,7 +41,6 @@ {% endif %} {% endif %} - {% if mod|hasPermission(config.mod.move, board.uri) %} {% if not post.thread %} {{ config.mod.link_move }}  @@ -49,6 +48,13 @@ {{ config.mod.link_move }}  {% endif %} {% endif %} +{% if mod|hasPermission(config.mod.cycle, board.uri) %} + {% if post.cycle %} + {{ config.mod.link_uncycle }}  + {% else %} + {{ config.mod.link_cycle }}  + {% endif %} +{% endif %} {% if mod|hasPermission(config.mod.editpost, board.uri) %} {{ config.mod.link_editpost }}  {% endif %} diff --git a/templates/post_thread.html b/templates/post_thread.html index 72ca2998..125d5f78 100644 --- a/templates/post_thread.html +++ b/templates/post_thread.html @@ -19,25 +19,32 @@ {{ post.id }} {% if post.sticky %} {% if config.font_awesome %} - + {% else %} Sticky {% endif %} {% endif %} {% if post.locked %} {% if config.font_awesome %} - + {% else %} Locked {% endif %} {% endif %} {% if post.bumplocked and (config.mod.view_bumplock < 0 or (post.mod and post.mod|hasPermission(config.mod.view_bumplock, board.uri))) %} {% if config.font_awesome %} - + {% else %} Bumplocked {% endif %} {% endif %} + {% if post.cycle %} + {% if config.font_awesome %} + + {% else %} + Cyclical + {% endif %} + {% endif %} {% if index %} [{% trans %}Reply{% endtrans %}] {% endif %} diff --git a/templates/posts.sql b/templates/posts.sql index 6b2249ef..070e687b 100644 --- a/templates/posts.sql +++ b/templates/posts.sql @@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS ``posts_{{ board }}`` ( `ip` varchar(39) CHARACTER SET ascii NOT NULL, `sticky` int(1) NOT NULL, `locked` int(1) NOT NULL, + `cycle` int(1) NOT NULL, `sage` int(1) NOT NULL, `embed` text, `slug` varchar(256) DEFAULT NULL, From a5bd39dc4ab2394db7f87d80a01480b22e4a74e1 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 16:49:35 +0200 Subject: [PATCH 123/212] mod dashboard html: link to page editor --- templates/mod/dashboard.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/templates/mod/dashboard.html b/templates/mod/dashboard.html index 5c44a522..d13bf7f2 100644 --- a/templates/mod/dashboard.html +++ b/templates/mod/dashboard.html @@ -19,6 +19,9 @@ {% if mod|hasPermission(config.mod.manageboards) %} [{% trans 'edit' %}] {% endif %} + {% if mod|hasPermission(config.mod.edit_pages) %} + [{% trans 'pages' %}] + {% endif %}
  • {% endfor %} @@ -100,6 +103,9 @@ {% if mod|hasPermission(config.mod.modlog) %}
  • {% trans 'Moderation log' %}
  • {% endif %} + {% if mod|hasPermission(config.mod.edit_pages) %} +
  • {% trans 'Global static pages' %}
  • + {% endif %} {% if mod|hasPermission(config.mod.recent) %}
  • {% trans 'Recent posts' %}
  • {% endif %} From 3571670b98b2e6293e429f3e629510ab9f38e70a Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 16:51:34 +0200 Subject: [PATCH 124/212] fix catalog link someone? --- templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/index.html b/templates/index.html index d1871fc8..1be614e3 100644 --- a/templates/index.html +++ b/templates/index.html @@ -69,7 +69,7 @@ [{{ page.num }}]{% if loop.last %} {% endif %} {% endfor %} {{ btn.next }} {% if config.catalog_link %} - | Catalog + | {% trans %}Catalog{% endtrans %} {% endif %}
    From 3f405b3484ae6b621ca1fdacbf20e1ef9f3b553a Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 16:53:28 +0200 Subject: [PATCH 125/212] what if IP address contained bad characters? (highly local) --- templates/mod/ban_form.html | 2 +- templates/mod/view_ip.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/mod/ban_form.html b/templates/mod/ban_form.html index 6c7560ad..98cc34b2 100644 --- a/templates/mod/ban_form.html +++ b/templates/mod/ban_form.html @@ -21,7 +21,7 @@ {% if not hide_ip %} - + {% else %} {% trans 'hidden' %} {% endif %} diff --git a/templates/mod/view_ip.html b/templates/mod/view_ip.html index 1c1c7fa6..4bacc7f6 100644 --- a/templates/mod/view_ip.html +++ b/templates/mod/view_ip.html @@ -45,7 +45,7 @@ {% if mod|hasPermission(config.mod.remove_notes) %} - + [{% trans 'remove' %}] From ce9f9eec252c435bfc1a9d637ae23c5178f9c1e4 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 17:11:12 +0200 Subject: [PATCH 126/212] settings dialog refinements: now you can resize it --- stylesheets/style.css | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/stylesheets/style.css b/stylesheets/style.css index b36a9de9..39d1fd04 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -819,10 +819,14 @@ pre { } #options_div { - width: 600px; - height: 320px; + width: 620px; + height: 400px; + resize: both; + overflow: auto; } + + #alert_div { width: 500px; } @@ -853,7 +857,7 @@ pre { #options_tablist { padding: 0px 5px; left: 0px; - width: 70px; + width: 90px; top: 0px; bottom: 0px; height: 100%; @@ -882,11 +886,12 @@ pre { padding: 10px; position: absolute; top: 0px; - bottom: 0px; - left: 81px; + bottom: 10px; + left: 101px; right: 0px; text-align: left; font-size: 12px; + overflow-y: auto; } .options_tab h2 { From deef54fe13c181181b62f8e7e5ec8953e62a6f72 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 6 May 2016 18:42:33 +0200 Subject: [PATCH 127/212] introduce smart_build_helper --- smart_build.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smart_build.php b/smart_build.php index 31d8110a..e287a31a 100644 --- a/smart_build.php +++ b/smart_build.php @@ -1,8 +1,8 @@ bindValue(':id', $thread); if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) { - Cache::set("thread_exists_".$b."_".$thread, "no"); + Cache::set("thread_exists_".$b."_".$thread, "no", 3600); return false; } From a5e22f6d637365042c53f5e9552e8e8444549d6f Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 02:50:44 +0200 Subject: [PATCH 128/212] split route and controller parts from smart build --- inc/controller.php | 108 ++++++++++++++++++++++++++++++++ inc/route.php | 62 ++++++++++++++++++ smart_build.php | 153 +++------------------------------------------ 3 files changed, 179 insertions(+), 144 deletions(-) create mode 100644 inc/controller.php create mode 100644 inc/route.php diff --git a/inc/controller.php b/inc/controller.php new file mode 100644 index 00000000..02e33443 --- /dev/null +++ b/inc/controller.php @@ -0,0 +1,108 @@ + $config['max_pages']) return false; + $config['try_smarter'] = true; + $build_pages = array($page); + buildIndex("skip"); + return true; +} + +function sb_api_board($b, $page = 0) { $page = (int)$page; + return sb_board($b, $page + 1); +} + +function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread; + if ($thread < 1) return false; + + if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false; + + if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false; + + $query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b)); + if (!$query->execute()) return false; + + $s = $query->fetch(PDO::FETCH_ASSOC); + $max = $s['max']; + + if ($thread > $max) return false; + + $query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b)); + $query->bindValue(':id', $thread); + + if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) { + Cache::set("thread_exists_".$b."_".$thread, "no", 3600); + return false; + } + + if ($slugcheck && $config['slugify']) { + global $request; + + $link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b)); + $link = "/".$b."/".$config['dir']['res'].$link; + + if ($link != $request) { + header("Location: $link", true, 301); + die(); + } + } + + if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway + global $request; + $r = str_replace("+50", "", $request); + $r = substr($r, 1); // Cut the slash + + if (file_exists($r)) return false; + } + + if (!openBoard($b)) return false; + buildThread($thread); + return true; +} + +function sb_thread_slugcheck($b, $thread) { + return sb_thread($b, $thread, true); +} +function sb_thread_slugcheck50($b, $thread) { + return sb_thread($b, $thread, 50); +} + +function sb_api($b) { global $config, $build_pages; + if (!openBoard($b)) return false; + $config['try_smarter'] = true; + $build_pages = array(-1); + buildIndex(); + return true; +} + +function sb_ukko() { + rebuildTheme("ukko", "post-thread"); + return true; +} + +function sb_catalog($b) { + if (!openBoard($b)) return false; + + rebuildTheme("catalog", "post-thread", $b); + return true; +} + +function sb_recent() { + rebuildTheme("recent", "post-thread"); + return true; +} + +function sb_sitemap() { + rebuildTheme("sitemap", "all"); + return true; +} + diff --git a/inc/route.php b/inc/route.php new file mode 100644 index 00000000..66602d77 --- /dev/null +++ b/inc/route.php @@ -0,0 +1,62 @@ + $fun) { + $id = '@^' . preg_quote($id, '@') . '$@u'; + + $id = str_replace('%b', '('.$config['board_regex'].')', $id); + $id = str_replace('%d', '([0-9]+)', $id); + $id = str_replace('%s', '[a-zA-Z0-9-]+', $id); + + $matches = null; + + if (preg_match ($id, $request, $matches)) { + array_shift($matches); + + $reached = array($fun, $matches); + + break; + } + } + + return $reached; +} + diff --git a/smart_build.php b/smart_build.php index e287a31a..58596055 100644 --- a/smart_build.php +++ b/smart_build.php @@ -1,5 +1,7 @@ $config['max_pages']) return false; - $config['try_smarter'] = true; - $build_pages = array($page); - buildIndex("skip"); - return true; -} - -function sb_api_board($b, $page = 0) { $page = (int)$page; - return sb_board($b, $page + 1); -} - -function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread; - if ($thread < 1) return false; - - if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false; - - if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false; - - $query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b)); - if (!$query->execute()) return false; - - $s = $query->fetch(PDO::FETCH_ASSOC); - $max = $s['max']; - - if ($thread > $max) return false; - - $query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b)); - $query->bindValue(':id', $thread); - - if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) { - Cache::set("thread_exists_".$b."_".$thread, "no", 3600); - return false; - } - - if ($slugcheck && $config['slugify']) { - global $request; - - $link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b)); - $link = "/".$b."/".$config['dir']['res'].$link; - - if ($link != $request) { - header("Location: $link", true, 301); - die(); - } - } - - if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway - global $request; - $r = str_replace("+50", "", $request); - $r = substr($r, 1); // Cut the slash - - if (file_exists($r)) return false; - } - - if (!openBoard($b)) return false; - buildThread($thread); - return true; -} - -function sb_thread_slugcheck($b, $thread) { - return sb_thread($b, $thread, true); -} -function sb_thread_slugcheck50($b, $thread) { - return sb_thread($b, $thread, 50); -} - -function sb_api($b) { global $config, $build_pages; - if (!openBoard($b)) return false; - $config['try_smarter'] = true; - $build_pages = array(-1); - buildIndex(); - return true; -} - -function sb_ukko() { - rebuildTheme("ukko", "post-thread"); - return true; -} - -function sb_catalog($b) { - if (!openBoard($b)) return false; - - rebuildTheme("catalog", "post-thread", $b); - return true; -} - -function sb_recent() { - rebuildTheme("recent", "post-thread"); - return true; -} - -function sb_sitemap() { - rebuildTheme("sitemap", "all"); - return true; -} - -$entrypoints = array(); - -$entrypoints['/%b/'] = 'sb_board'; -$entrypoints['/%b/'.$config['file_index']] = 'sb_board'; -$entrypoints['/%b/'.$config['file_page']] = 'sb_board'; -$entrypoints['/%b/%d.json'] = 'sb_api_board'; -if ($config['api']['enabled']) { - $entrypoints['/%b/threads.json'] = 'sb_api'; - $entrypoints['/%b/catalog.json'] = 'sb_api'; -} - -$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck'; -$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50'; -if ($config['slugify']) { - $entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck'; - $entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50'; -} -if ($config['api']['enabled']) { - $entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread'; -} - -$entrypoints['/*/'] = 'sb_ukko'; -$entrypoints['/*/index.html'] = 'sb_ukko'; -$entrypoints['/recent.html'] = 'sb_recent'; -$entrypoints['/%b/catalog.html'] = 'sb_catalog'; -$entrypoints['/sitemap.xml'] = 'sb_sitemap'; - -$reached = false; - $request = $_SERVER['REQUEST_URI']; -list($request) = explode('?', $request); -foreach ($entrypoints as $id => $fun) { - $id = '@^' . preg_quote($id, '@') . '$@u'; +$route = route($request); - $id = str_replace('%b', '('.$config['board_regex'].')', $id); - $id = str_replace('%d', '([0-9]+)', $id); - $id = str_replace('%s', '[a-zA-Z0-9-]+', $id); - - $matches = null; - - if (preg_match ($id, $request, $matches)) { - array_shift($matches); - - $reached = call_user_func_array($fun, $matches); - - break; - } +if (!$route) { + $reached = false; +} +else { + list ($fun, $args) = $route; + $reached = call_user_func_array($route); } function die_404() { global $config; From 644f227ab33f247dc82e0208fe2125d5d002f4f4 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 03:09:20 +0200 Subject: [PATCH 129/212] fix "Undefined variable: pid"; thanks fpdl and MrFreeman --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index 98aefe05..4ea08ce3 100644 --- a/post.php +++ b/post.php @@ -955,7 +955,7 @@ if (isset($_POST['delete'])) { $build_pages = range(1, $config['max_pages']); if ($post['op']) - clean($pid); + clean($id); event('post-after', $post); From b6f0317bde1b6d9a9ac5d81a660865a7aa9ad13d Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 10:54:30 +0200 Subject: [PATCH 130/212] advanced build (1/2): a small refactor of index generating procedure; generation strategies --- inc/config.php | 70 ++++++++++++++++++-- inc/functions.php | 102 +++++++++++++++++++++-------- inc/route.php | 5 +- smart_build.php | 9 ++- templates/themes/catalog/theme.php | 13 ++-- templates/themes/recent/theme.php | 5 +- templates/themes/sitemap/theme.php | 6 +- templates/themes/ukko/theme.php | 6 +- 8 files changed, 169 insertions(+), 47 deletions(-) diff --git a/inc/config.php b/inc/config.php index 82fdc882..91804429 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1203,16 +1203,74 @@ // Try not to build pages when we shouldn't have to. $config['try_smarter'] = true; - // EXPERIMENTAL: Defer static HTML building to a moment, when a given file is actually accessed. - // Warning: This option won't run out of the box. You need to tell your webserver, that a file - // for serving 403 and 404 pages is /smart_build.php. Also, you need to turn off indexes. +/* + * ==================== + * Advanced build + * ==================== + */ + + // Strategies for file generation. Also known as an "advanced build". If you don't have performance + // issues, you can safely ignore that part, because it's hard to configure and won't even work on + // your free webhosting. + // + // A strategy is a function, that given the PHP environment and ($fun, $array) variable pair, returns + // an $action array or false. + // + // $fun - a controller function name, see inc/controller.php. This is named after functions, so that + // we can generate the files in daemon. + // + // $array - arguments to be passed + // + // $action - action to be taken. It's an array, and the first element of it is one of the following: + // * "immediate" - generate the page immediately + // * "defer" - defer page generation to a moment a worker daemon gets to build it (serving a stale + // page in the meantime). The remaining arguments are daemon-specific. Daemon isn't + // implemented yet :DDDD inb4 while(true) { generate(Queue::Get()) }; (which is probably it). + // * "build_on_load" - defer page generation to a moment, when the user actually accesses the page. + // This is a smart_build behaviour. You shouldn't use this one too much, if you + // use it for active boards, the server may choke due to a possible race condition. + // See my blog post: https://engine.vichan.net/blog/res/2.html + // + // So, let's assume we want to build a thread 1324 on board /b/, because a new post appeared there. + // We try the first strategy, giving it arguments: 'sb_thread', array('b', 1324). The strategy will + // now return a value $action, denoting an action to do. If $action is false, we try another strategy. + // + // As I said, configuration isn't easy. + // + // 1. chmod 0777 directories: tmp/locks/ and tmp/queue/. + // 2. serve 403 and 404 requests to go thru smart_build.php + // for nginx, this blog post contains config snippets: https://engine.vichan.net/blog/res/2.html + // 3. disable indexes in your webserver + // 4. launch any number of daemons (eg. twice your number of threads?) using the command: + // $ tools/worker.php + // You don't need to do that step if you are not going to use the "defer" option. + // 5. enable smart_build_helper (see below) + // 6. edit the strategies (see inc/functions.php for the builtin ones). You can use lambdas. I will test + // various ones and include one that works best for me. + $config['generation_strategies'] = array(); + // Add a sane strategy. It forces to immediately generate a page user is about to land on. Otherwise, + // it has no opinion, so it needs a fallback strategy. + $config['generation_strategies'][] = 'strategy_sane'; + // Add an immediate catch-all strategy. This is the default function of imageboards: generate all pages + // on post time. + $config['generation_strategies'][] = 'strategy_immediate'; + // NOT RECOMMENDED: Instead of an all-"immediate" strategy, you can use an all-"build_on_load" one (used + // to be initialized using $config['smart_build']; ) for all pages instead of those to be build + // immediately. A rebuild done in this mode should remove all your static files + // $config['generation_strategies'][1] = 'strategy_smart_build'; + + // Deprecated. Leave it false. See above. $config['smart_build'] = false; - // Smart build related: when a file doesn't exist, where should we redirect? + // Use smart_build.php for dispatching missing requests. It may be useful without smart_build or advanced + // build, for example it will regenerate the missing files. + $config['smart_build_helper'] = true; + + // smart_build.php: when a file doesn't exist, where should we redirect? $config['page_404'] = '/404.html'; - // Smart build related: extra entrypoints. - $config['smart_build_entrypoints'] = array(); + // Extra controller entrypoints. Controller is used only by smart_build and advanced build. + $config['controller_entrypoints'] = array(); /* * ==================== diff --git a/inc/functions.php b/inc/functions.php index 1bd1f32f..2957c680 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1319,7 +1319,8 @@ function thread_find_page($thread) { return floor(($config['threads_per_page'] + $index) / $config['threads_per_page']); } -function index($page, $mod=false) { +// $brief means that we won't need to generate anything yet +function index($page, $mod=false, $brief = false) { global $board, $config, $debug; $body = ''; @@ -1350,6 +1351,7 @@ function index($page, $mod=false) { 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']); @@ -1389,7 +1391,10 @@ function index($page, $mod=false) { } $threads[] = $thread; - $body .= $thread->build(true); + + if (!$brief) { + $body .= $thread->build(true); + } } if ($config['file_board']) { @@ -1610,27 +1615,28 @@ function checkMute() { function buildIndex($global_api = "yes") { global $board, $config, $build_pages; - if (!$config['smart_build']) { - $pages = getPages(); - if (!$config['try_smarter']) - $antibot = create_antibot($board['uri']); + $catalog_api_action = generation_strategy('sb_api', array($board['uri'])); - if ($config['api']['enabled']) { - $api = new Api(); - $catalog = array(); - } + $pages = null; + $antibot = null; + + if ($config['api']['enabled']) { + $api = new Api(); + $catalog = array(); } for ($page = 1; $page <= $config['max_pages']; $page++) { $filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page)); $jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0 - if ((!$config['api']['enabled'] || $global_api == "skip" || $config['smart_build']) && $config['try_smarter'] - && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) ) + $wont_build_this_page = $config['try_smarter'] && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages); + + if ((!$config['api']['enabled'] || $global_api == "skip") && $wont_build_this_page) continue; - if (!$config['smart_build']) { - $content = index($page); + $action = generation_strategy('sb_board', array($board['uri'], $page)); + if ($action == 'rebuild' || $catalog_api_action == 'rebuild') { + $content = index($page, false, $wont_build_this_page); if (!$content) break; @@ -1641,17 +1647,21 @@ function buildIndex($global_api = "yes") { file_write($jsonFilename, $json); $catalog[$page-1] = $threads; - } - if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages) - && !empty($build_pages) && !in_array($page, $build_pages) ) - continue; + if ($wont_build_this_page) continue; + } if ($config['try_smarter']) { $antibot = create_antibot($board['uri'], 0 - $page); $content['current_page'] = $page; } + elseif (!$antibot) { + create_antibot($board['uri']); + } $antibot->reset(); + if (!$pages) { + $pages = getPages(); + } $content['pages'] = $pages; $content['pages'][$page-1]['selected'] = true; $content['btn'] = getPageButtons($content['pages']); @@ -1659,13 +1669,14 @@ function buildIndex($global_api = "yes") { file_write($filename, Element('index.html', $content)); } - else { + elseif ($action == 'delete' || $catalog_api_action == 'delete') { file_unlink($filename); file_unlink($jsonFilename); } } - if (!$config['smart_build'] && $page < $config['max_pages']) { + // $action is an action for our last page + if (($catalog_api_action == 'rebuild' || $action == 'rebuild' || $action == 'delete') && $page < $config['max_pages']) { for (;$page<=$config['max_pages'];$page++) { $filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page)); file_unlink($filename); @@ -1679,13 +1690,13 @@ function buildIndex($global_api = "yes") { // json api catalog if ($config['api']['enabled'] && $global_api != "skip") { - if ($config['smart_build']) { + if ($catalog_api_action == 'delete') { $jsonFilename = $board['dir'] . 'catalog.json'; file_unlink($jsonFilename); $jsonFilename = $board['dir'] . 'threads.json'; file_unlink($jsonFilename); } - else { + elseif ($catalog_api_action == 'rebuild') { $json = json_encode($api->translateCatalog($catalog)); $jsonFilename = $board['dir'] . 'catalog.json'; file_write($jsonFilename, $json); @@ -2204,7 +2215,9 @@ function buildThread($id, $return = false, $mod = false) { if ($config['try_smarter'] && !$mod) $build_pages[] = thread_find_page($id); - if (!$config['smart_build'] || $return || $mod) { + $action = generation_strategy('sb_thread', array($board['uri'], $id)); + + if ($action == 'rebuild' || $return || $mod) { $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); @@ -2239,26 +2252,26 @@ function buildThread($id, $return = false, $mod = false) { )); // json api - if ($config['api']['enabled']) { + if ($config['api']['enabled'] && !$mod) { $api = new Api(); $json = json_encode($api->translateThread($thread)); $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; file_write($jsonFilename, $json); } } - else { + elseif($action == 'delete') { $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json'; file_unlink($jsonFilename); } - if ($config['smart_build'] && !$return && !$mod) { + if ($action == 'delete' && !$return && !$mod) { $noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true); file_unlink($noko50fn); file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id))); - } else if ($return) { + } elseif ($return) { return $body; - } else { + } elseif ($action == 'rebuild') { $noko50fn = $board['dir'] . $config['dir']['res'] . link_for($thread, true); if ($hasnoko50 || file_exists($noko50fn)) { buildThread50($id, $return, $mod, $thread, $antibot); @@ -2788,3 +2801,36 @@ function markdown($s) { return $pd->text($s); } + +function generation_strategy($fun, $array=array()) { global $config; + $action = false; + + foreach ($config['generation_strategies'] as $s) { + if ($strategy = $s($fun, $array)) { + break; + } + } + + switch ($strategy[0]) { + case 'immediate': + return 'rebuild'; + case 'defer': + // Ok, it gets interesting here :) + Queue::add(serialize(array('build', $fun, $array))); + return 'ignore'; + case 'build_on_load': + return 'delete'; + } +} + +function strategy_immediate($fun, $array) { + return array('immediate'); +} + +function strategy_smart_build($fun, $array) { + return array('build_on_load'); +} + +function strategy_sane($fun, $array) { global $config; + return false; +} diff --git a/inc/route.php b/inc/route.php index 66602d77..2a5c1732 100644 --- a/inc/route.php +++ b/inc/route.php @@ -7,7 +7,7 @@ defined('TINYBOARD') or exit; -function route($path) { +function route($path) { global $config; $entrypoints = array(); $entrypoints['/%b/'] = 'sb_board'; @@ -33,8 +33,11 @@ function route($path) { $entrypoints['/*/index.html'] = 'sb_ukko'; $entrypoints['/recent.html'] = 'sb_recent'; $entrypoints['/%b/catalog.html'] = 'sb_catalog'; + $entrypoints['/%b/index.rss'] = 'sb_catalog'; $entrypoints['/sitemap.xml'] = 'sb_sitemap'; + $entrypoints = array_merge($entrypoints, $config['controller_entrypoints']); + $reached = false; list($request) = explode('?', $path); diff --git a/smart_build.php b/smart_build.php index 58596055..7ca5fcbf 100644 --- a/smart_build.php +++ b/smart_build.php @@ -3,14 +3,16 @@ require_once("inc/functions.php"); require_once("inc/route.php"); require_once("inc/controller.php"); -if (!$config['smart_build'] && !$config["smart_build_helper"]) { - die('You need to enable $config["smart_build"] or $config["smart_build_helper"]'); +if (!$config["smart_build_helper"]) { + die('You need to enable $config["smart_build_helper"]'); } $config['smart_build'] = false; // Let's disable it, so we can build the page for real +$config['generation_strategies'] = array('strategy_immediate'); function after_open_board() { global $config; $config['smart_build'] = false; + $config['generation_strategies'] = array('strategy_immediate'); }; $request = $_SERVER['REQUEST_URI']; @@ -59,6 +61,9 @@ if ($reached) { elseif (preg_match('/\.xml$/', $request)) { header("Content-Type", "application/xml"); } + elseif (preg_match('/\.rss$/', $request)) { + header("Content-Type", "application/rss+xml"); + } else { header("Content-Type", "text/html; charset=utf-8"); } diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php index 239d4dff..4f512c03 100644 --- a/templates/themes/catalog/theme.php +++ b/templates/themes/catalog/theme.php @@ -16,20 +16,25 @@ if ($action == 'all') { foreach ($boards as $board) { $b = new Catalog(); - if ($config['smart_build']) { + + $action = generation_strategy("sb_catalog", array($board)); + if ($action == 'delete') { file_unlink($config['dir']['home'] . $board . '/catalog.html'); + file_unlink($config['dir']['home'] . $board . '/index.rss'); } - else { + elseif ($action == 'rebuild') { $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)) { $b = new Catalog(); - if ($config['smart_build']) { + $action = generation_strategy("sb_catalog", array($board)); + if ($action == 'delete') { file_unlink($config['dir']['home'] . $board . '/catalog.html'); + file_unlink($config['dir']['home'] . $board . '/index.rss'); } - else { + elseif ($action == 'rebuild') { $b->build($settings, $board); } } diff --git a/templates/themes/recent/theme.php b/templates/themes/recent/theme.php index f44e2529..95921c35 100644 --- a/templates/themes/recent/theme.php +++ b/templates/themes/recent/theme.php @@ -25,10 +25,11 @@ $this->excluded = explode(' ', $settings['exclude']); if ($action == 'all' || $action == 'post' || $action == 'post-thread' || $action == 'post-delete') { - if ($config['smart_build']) { + $action = generation_strategy('sb_recent', array()); + if ($action == 'delete') { file_unlink($config['dir']['home'] . $settings['html']); } - else { + elseif ($action == 'rebuild') { file_write($config['dir']['home'] . $settings['html'], $this->homepage($settings)); } } diff --git a/templates/themes/sitemap/theme.php b/templates/themes/sitemap/theme.php index 52779d53..6bc035bb 100644 --- a/templates/themes/sitemap/theme.php +++ b/templates/themes/sitemap/theme.php @@ -23,10 +23,12 @@ } } - if ($config['smart_build']) { + $action = generation_strategy('sb_sitemap', array()); + + if ($action == 'delete') { file_unlink($settings['path']); } - else { + elseif ($action == 'rebuild') { $boards = explode(' ', $settings['boards']); $threads = array(); diff --git a/templates/themes/ukko/theme.php b/templates/themes/ukko/theme.php index d6fc303c..e572c467 100644 --- a/templates/themes/ukko/theme.php +++ b/templates/themes/ukko/theme.php @@ -11,10 +11,12 @@ return; } - if ($config['smart_build']) { + $action = generation_strategy('sb_ukko', array()); + + if ($action == 'delete') { file_unlink($settings['uri'] . '/index.html'); } - else { + elseif ($action == 'rebuild') { file_write($settings['uri'] . '/index.html', $ukko->build()); } } From e2653754750917035b79e62b07d286b3a56cf9ca Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 10:59:36 +0200 Subject: [PATCH 131/212] fixup --- inc/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/functions.php b/inc/functions.php index 2957c680..8329e774 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1656,7 +1656,7 @@ function buildIndex($global_api = "yes") { $content['current_page'] = $page; } elseif (!$antibot) { - create_antibot($board['uri']); + $antibot = create_antibot($board['uri']); } $antibot->reset(); if (!$pages) { From 12e6aba5d4a2965591b3df76e4f6c19e35475700 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 13:20:00 +0200 Subject: [PATCH 132/212] (2/2) advanced build. implement a daemon that will build static pages. implement a queue and a lock. fix notice in bans. and it even works! the daemon is basic right now, it could work in a mode that it will defer building certain pages until a certain time. --- inc/bans.php | 2 +- inc/config.php | 9 ++++++++- inc/functions.php | 40 ++++++++++++++++++++++++++++++++----- smart_build.php | 2 +- tmp/queue/generate/.gitkeep | 0 tools/worker.php | 31 ++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 tmp/queue/generate/.gitkeep create mode 100755 tools/worker.php diff --git a/inc/bans.php b/inc/bans.php index 87e06e28..c468eb64 100644 --- a/inc/bans.php +++ b/inc/bans.php @@ -166,7 +166,7 @@ class Bans { if ($ban['post']) { $post = json_decode($ban['post']); - $ban['message'] = $post->body; + $ban['message'] = isset($post->body) ? $post->body : 0; } unset($ban['ipstart'], $ban['ipend'], $ban['post'], $ban['creator']); diff --git a/inc/config.php b/inc/config.php index 91804429..5926eb1d 100644 --- a/inc/config.php +++ b/inc/config.php @@ -103,7 +103,7 @@ /* * ==================== - * Cache settings + * Cache, lock and queue settings * ==================== */ @@ -120,6 +120,7 @@ // $config['cache']['enabled'] = 'apc'; // $config['cache']['enabled'] = 'memcached'; // $config['cache']['enabled'] = 'redis'; + // $config['cache']['enabled'] = 'fs'; // Timeout for cached objects such as posts and HTML. $config['cache']['timeout'] = 60 * 60 * 48; // 48 hours @@ -142,6 +143,12 @@ // (this file will be explicitly loaded during cache hit, but not during cache miss). $config['cache_config'] = false; + // Define a lock driver. + $config['lock']['enabled'] = 'fs'; + + // Define a queue driver. + $config['queue']['enabled'] = 'fs'; // xD + /* * ==================== * Cookie settings diff --git a/inc/functions.php b/inc/functions.php index 8329e774..0cfdb72f 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -19,6 +19,8 @@ require_once 'inc/database.php'; require_once 'inc/events.php'; require_once 'inc/api.php'; require_once 'inc/mod/auth.php'; +require_once 'inc/lock.php'; +require_once 'inc/queue.php'; require_once 'inc/polyfill.php'; @include_once 'inc/lib/parsedown/Parsedown.php'; // fail silently, this isn't a critical piece of code @@ -93,6 +95,8 @@ function loadConfig() { 'db', 'api', 'cache', + 'lock', + 'queue', 'cookies', 'error', 'dir', @@ -1749,7 +1753,6 @@ function buildJavascript() { function checkDNSBL() { global $config; - if (isIPv6()) return; // No IPv6 support yet. @@ -2806,17 +2809,17 @@ function generation_strategy($fun, $array=array()) { global $config; $action = false; foreach ($config['generation_strategies'] as $s) { - if ($strategy = $s($fun, $array)) { + if ($action = $s($fun, $array)) { break; } } - switch ($strategy[0]) { + switch ($action[0]) { case 'immediate': return 'rebuild'; case 'defer': // Ok, it gets interesting here :) - Queue::add(serialize(array('build', $fun, $array))); + get_queue('generate')->push(serialize(array('build', $fun, $array, $action))); return 'ignore'; case 'build_on_load': return 'delete'; @@ -2832,5 +2835,32 @@ function strategy_smart_build($fun, $array) { } function strategy_sane($fun, $array) { global $config; - return false; + // Well, ideally a sane strategy would involve a more stringent checking, + // but let's at least have something to get the ball rolling :^) + + if (php_sapi_name() == 'cli') return false; + else if (isset($_POST['mod']) || isset($_POST['json_response'])) return false; + else if ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1)) return array('immediate'); + else return false; +} + +// My first, test strategy. +function strategy_first($fun, $array) { + switch ($fun) { + case 'sb_thread': + return array('defer'); + case 'sb_board': + if ($array[1] > 8) return array('build_on_load'); + else return array('defer'); + case 'sb_api': + return array('defer'); + case 'sb_catalog': + return array('defer'); + case 'sb_recent': + return array('build_on_load'); + case 'sb_sitemap': + return array('build_on_load'); + case 'sb_ukko': + return array('defer'); + } } diff --git a/smart_build.php b/smart_build.php index 7ca5fcbf..cfac446a 100644 --- a/smart_build.php +++ b/smart_build.php @@ -24,7 +24,7 @@ if (!$route) { } else { list ($fun, $args) = $route; - $reached = call_user_func_array($route); + $reached = call_user_func_array($fun, $args); } function die_404() { global $config; diff --git a/tmp/queue/generate/.gitkeep b/tmp/queue/generate/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tools/worker.php b/tools/worker.php new file mode 100755 index 00000000..e19fe1c6 --- /dev/null +++ b/tools/worker.php @@ -0,0 +1,31 @@ +#!/usr/bin/php +pop(2); + foreach ($q as $v) { + list($__, $func, $ary, $action) = unserialize($v); + echo "Starting to generate $func ".implode(" ", $ary)."... "; + + call_user_func_array($func, $ary); + + echo "done!\n"; + } + if (!$q) usleep(20000); // 0.02s +} From f24e0f98143528c7b2cd5c504899c5492f5d75fd Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 14:01:55 +0200 Subject: [PATCH 133/212] =?UTF-8?q?optimize=20out=20openboard=20when=20we?= =?UTF-8?q?=20don`t=20need=20it.=20a=20big=20performance=20improvement=20t?= =?UTF-8?q?oo=20=F0=9F=8F=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also, don't call dnsbl for local ip addresses --- inc/functions.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 0cfdb72f..35be56b9 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -556,14 +556,19 @@ function setupBoard($array) { } function openBoard($uri) { - global $config, $build_pages; + global $config, $build_pages, $board; if ($config['try_smarter']) $build_pages = array(); - $board = getBoardInfo($uri); - if ($board) { - setupBoard($board); + // And what if we don't really need to change a board we have opened? + if (isset ($board) && isset ($board['uri']) && $board['uri'] == $uri) { + return true; + } + + $b = getBoardInfo($uri); + if ($b) { + setupBoard($b); if (function_exists('after_open_board')) { after_open_board(); @@ -1759,6 +1764,9 @@ function checkDNSBL() { if (!isset($_SERVER['REMOTE_ADDR'])) return; // Fix your web server configuration + if (preg_match("/^(::(ffff:)?)?(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|0\.|255\.)/", $_SERVER['REMOTE_ADDR'])) + return; // It's pointless to check for local IP addresses in dnsbls, isn't it? + if (in_array($_SERVER['REMOTE_ADDR'], $config['dnsbl_exceptions'])) return; From bb9aaad899c47e4eec49cbd444573e78d48a146a Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 8 May 2016 15:37:49 +0200 Subject: [PATCH 134/212] i forgot about a queue and a lock implementation --- inc/lock.php | 39 +++++++++++++++++++++++++++++++++++++++ inc/queue.php | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 inc/lock.php create mode 100644 inc/queue.php diff --git a/inc/lock.php b/inc/lock.php new file mode 100644 index 00000000..4fb2f5df --- /dev/null +++ b/inc/lock.php @@ -0,0 +1,39 @@ +f = fopen("tmp/locks/$key", "w"); + } + } + + // Get a shared lock + function get($nonblock = false) { global $config; + if ($config['lock']['enabled'] == 'fs') { + $wouldblock = false; + flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock); + if ($nonblock && $wouldblock) return false; + } + return $this; + } + + // Get an exclusive lock + function get_ex($nonblock = false) { global $config; + if ($config['lock']['enabled'] == 'fs') { + $wouldblock = false; + flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock); + if ($nonblock && $wouldblock) return false; + } + return $this; + } + + // Free a lock + function free() { global $config; + if ($config['lock']['enabled'] == 'fs') { + flock($this->f, LOCK_UN); + } + return $this; + } +} diff --git a/inc/queue.php b/inc/queue.php new file mode 100644 index 00000000..66305b3b --- /dev/null +++ b/inc/queue.php @@ -0,0 +1,49 @@ +lock = new Lock($key); + $key = str_replace('/', '::', $key); + $key = str_replace("\0", '', $key); + $this->key = "tmp/queue/$key/"; + } + } + + function push($str) { global $config; + if ($config['queue']['enabled'] == 'fs') { + $this->lock->get_ex(); + file_put_contents($this->key.microtime(true), $str); + $this->lock->free(); + } + return $this; + } + + function pop($n = 1) { global $config; + if ($config['queue']['enabled'] == 'fs') { + $this->lock->get_ex(); + $dir = opendir($this->key); + $paths = array(); + while ($n > 0) { + $path = readdir($dir); + if ($path === FALSE) break; + elseif ($path == '.' || $path == '..') continue; + else { $paths[] = $path; $n--; } + } + $out = array(); + foreach ($paths as $v) { + $out []= file_get_contents($this->key.$v); + unlink($this->key.$v); + } + $this->lock->free(); + return $out; + } + } +} + +// Don't use the constructor. Use the get_queue function. +$queues = array(); + +function get_queue($name) { global $queues; + return $queues[$name] = isset ($queues[$name]) ? $queues[$name] : new Queue($name); +} From ccc9cff23dfdcfb758d635438b0ff8f9fd476496 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 9 May 2016 10:59:50 +0200 Subject: [PATCH 135/212] ukko: post filters and reporting --- js/fix-report-delete-submit.js | 2 +- js/post-filter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/fix-report-delete-submit.js b/js/fix-report-delete-submit.js index abed4d06..61c127ab 100644 --- a/js/fix-report-delete-submit.js +++ b/js/fix-report-delete-submit.js @@ -8,7 +8,7 @@ * */ -if (active_page == 'thread' || active_page == 'index') { +if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko') { $(document).on('menu_ready', function(){ var Menu = window.Menu; diff --git a/js/post-filter.js b/js/post-filter.js index 950f43ab..0b2589f0 100644 --- a/js/post-filter.js +++ b/js/post-filter.js @@ -1,4 +1,4 @@ -if (active_page === 'thread' || active_page === 'index' || active_page === 'catalog') { +if (active_page === 'thread' || active_page === 'index' || active_page === 'catalog' || active_page === 'ukko') { $(document).on('menu_ready', function () { 'use strict'; From 1cff10fd953373abb546dd93f53acaee2eec19cd Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 9 May 2016 11:08:24 +0200 Subject: [PATCH 136/212] ukko & fix-re-de-su: fix reporting and deleting from ukko actually --- js/fix-report-delete-submit.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/fix-report-delete-submit.js b/js/fix-report-delete-submit.js index 61c127ab..f5cda9ad 100644 --- a/js/fix-report-delete-submit.js +++ b/js/fix-report-delete-submit.js @@ -20,6 +20,7 @@ if ($('#delete-fields #password').length) { var $ele = $(ele); var threadId = $ele.parent().attr('id').replace('thread_', ''); var postId = $ele.find('.post_no').not('[id]').text(); + var board_name = $ele.parent().data('board'); $buf.find('#delete_post_menu,#delete_file_menu').click(function(e) { e.preventDefault(); @@ -30,6 +31,7 @@ if ($('#delete-fields #password').length) { } else { $('#delete_file').prop('checked', ''); } + $('input[type="hidden"][name="board"]').val(board_name); $('input[name=delete][type=submit]').click(); }); }); @@ -42,6 +44,7 @@ Menu.onclick(function(e, $buf) { var $ele = $(ele); var threadId = $ele.parent().attr('id').replace('thread_', ''); var postId = $ele.find('.post_no').not('[id]').text(); + var board_name = $ele.parent().data('board'); $buf.find('#report_menu,#global_report_menu').click(function(e) { if ($(this).attr('id') === "global_report_menu") { From 4479fc7681d55afeeed90f9ddc4d892741bd6e53 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 9 May 2016 11:18:27 +0200 Subject: [PATCH 137/212] thread-watcher and favorites in catalog and ukko --- js/favorites.js | 2 +- js/thread-watcher.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/js/favorites.js b/js/favorites.js index daf7b732..027dc243 100644 --- a/js/favorites.js +++ b/js/favorites.js @@ -50,7 +50,7 @@ function add_favorites() { $('.boardlist').append(boards); }; -if (active_page == 'thread' || active_page == 'index') { +if (active_page == 'thread' || active_page == 'index' || active_page == 'catalog' || active_page == 'ukko') { $(document).ready(function(){ var favorites = JSON.parse(localStorage.favorites); var is_board_favorite = ~$.inArray(board_name, favorites); diff --git a/js/thread-watcher.js b/js/thread-watcher.js index c31a6e74..0796193b 100644 --- a/js/thread-watcher.js +++ b/js/thread-watcher.js @@ -41,7 +41,7 @@ watchlist.render = function(reset) { } else { //If the watchlist has not yet been rendered, create it. var menuStyle = getComputedStyle($('.boardlist')[0]); - $('form[name="post"]').before( + $((active_page == 'ukko') ? 'hr:first' : (active_page == 'catalog') ? 'body>span:first' : 'form[name="post"]').before( $('
    '+ '
    '+ '['+_('Clear List')+'] '+ @@ -60,6 +60,8 @@ watchlist.render = function(reset) { watchlist.add = function(sel) { var threadName, threadInfo; + var board_name = $(sel).parents('.thread').data('board'); + if (active_page === 'thread') { if ($('.subject').length){ //If a subject is given, use the first 20 characters as the thread name. @@ -70,7 +72,7 @@ watchlist.add = function(sel) { //board name, thread name as defined above, current amount of posts, thread url threadInfo = [board_name, threadName, $('.post').length, location.href]; - } else if (active_page === 'index') { + } else if (active_page === 'index' || active_page === 'ukko') { var postCount; //Figure out the post count. @@ -142,7 +144,7 @@ watchlist.exists = function(sel) { }; $(document).ready(function(){ - if (!(active_page == 'thread' || active_page == 'index')) { + if (!(active_page == 'thread' || active_page == 'index' || active_page == 'catalog' || active_page == 'ukko')) { return; } From 018dd48a66ec7734bad48105a32a2ae9a5f51017 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 9 May 2016 11:58:46 +0200 Subject: [PATCH 138/212] post-filter + ukko fixes --- js/post-filter.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/js/post-filter.js b/js/post-filter.js index 0b2589f0..3f2358e4 100644 --- a/js/post-filter.js +++ b/js/post-filter.js @@ -224,6 +224,7 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata var $ele = $(ele); var threadId = $ele.parent().attr('id').replace('thread_', ''); + var boardId = $ele.parent().data('board'); var postId = $ele.find('.post_no').not('[id]').text(); if (pageData.hasUID) { var postUid = $ele.find('.poster_id').text(); @@ -244,21 +245,21 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata $buffer.find('#filter-menu-unhide').click(function () { // if hidden due to post id, remove it from blacklist // otherwise just show this post - blacklist.remove.post(pageData.boardId, threadId, postId); + blacklist.remove.post(boardId, threadId, postId); show(ele); }); $buffer.find('#filter-menu-hide').addClass('hidden'); } else { $buffer.find('#filter-menu-unhide').addClass('hidden'); $buffer.find('#filter-menu-hide').click(function () { - blacklist.add.post(pageData.boardId, threadId, postId, false); + blacklist.add.post(boardId, threadId, postId, false); }); } // post id if (!$ele.data('hiddenByPost')) { $buffer.find('#filter-add-post-plus').click(function () { - blacklist.add.post(pageData.boardId, threadId, postId, true); + blacklist.add.post(boardId, threadId, postId, true); }); } else { $buffer.find('#filter-add-post-plus').addClass('hidden'); @@ -267,16 +268,16 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata // UID if (pageData.hasUID && !$ele.data('hiddenByUid')) { $buffer.find('#filter-add-id').click(function () { - blacklist.add.uid(pageData.boardId, threadId, postUid, false); + blacklist.add.uid(boardId, threadId, postUid, false); }); $buffer.find('#filter-add-id-plus').click(function () { - blacklist.add.uid(pageData.boardId, threadId, postUid, true); + blacklist.add.uid(boardId, threadId, postUid, true); }); $buffer.find('#filter-remove-id').addClass('hidden'); } else if (pageData.hasUID) { $buffer.find('#filter-remove-id').click(function () { - blacklist.remove.uid(pageData.boardId, threadId, postUid); + blacklist.remove.uid(boardId, threadId, postUid); }); $buffer.find('#filter-add-id').addClass('hidden'); @@ -350,12 +351,13 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata .click(function() { var postId = $(ele).find('.post_no').not('[id]').text(); var hidden = $(ele).data('hidden'); + var boardId = $(ele).parents('.thread').data('board'); if (hidden) { - blacklist.remove.post(pageData.boardId, threadId, postId, false); + blacklist.remove.post(boardId, threadId, postId, false); $(this).html('[–]'); } else { - blacklist.add.post(pageData.boardId, threadId, postId, false); + blacklist.add.post(boardId, threadId, postId, false); $(this).text('[+]'); } }); @@ -375,7 +377,9 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata var name, trip, uid, subject, comment; var i, length, array, rule, pattern; // temp variables - var boardId = pageData.boardId; + var boardId = $post.data('board'); + if (!boardId) boardId = $post.parents('.thread').data('board'); + var localList = pageData.localList; var noReplyList = pageData.noReplyList; var hasUID = pageData.hasUID; @@ -532,12 +536,13 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata return; var threadId = $thread.attr('id').replace('thread_', ''); + var boardId = $thread.data('board'); var op = $thread.children('.op')[0]; var i, array; // temp variables // add posts to localList and noReplyList - if (typeof list.postFilter[pageData.boardId] != 'undefined' && typeof list.postFilter[pageData.boardId][threadId] != 'undefined') { - array = list.postFilter[pageData.boardId][threadId]; + if (typeof list.postFilter[boardId] != 'undefined' && typeof list.postFilter[boardId][threadId] != 'undefined') { + array = list.postFilter[boardId][threadId]; for (i=0; i Date: Mon, 9 May 2016 13:02:21 +0200 Subject: [PATCH 139/212] post-filter & ukko: final solution i think --- js/post-filter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/post-filter.js b/js/post-filter.js index 3f2358e4..3bf55a51 100644 --- a/js/post-filter.js +++ b/js/post-filter.js @@ -179,7 +179,7 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata $ele.parent().find('.body, .files, .video-container').not($ele.children('.reply').children()).hide(); // hide thread replies on index view - if (active_page == 'index') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').hide(); + if (active_page == 'index' || active_page == 'ukko') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').hide(); } else { // normal posts $ele.children('.body, .files, .video-container').hide(); @@ -372,6 +372,7 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata */ function filter(post, threadId, pageData) { var $post = $(post); + var list = getList(); var postId = $post.find('.post_no').not('[id]').text(); var name, trip, uid, subject, comment; @@ -814,9 +815,10 @@ if (active_page === 'thread' || active_page === 'index' || active_page === 'cata var threadId; if ($(post).hasClass('reply')) { - threadId = $(post).parent().attr('id').replace('thread_', ''); + threadId = $(post).parents('.thread').attr('id').replace('thread_', ''); } else { threadId = $(post).attr('id').replace('thread_', ''); + post = $(post).children('.op')[0]; } filter(post, threadId, pageData); From 52fe9bc873e2950606afefad82cd102ad0a4db5c Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 15 May 2016 15:52:41 +0200 Subject: [PATCH 140/212] fix sane_strategy for advanced build. should fix the ajax.js problem. --- inc/functions.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 35be56b9..154386b0 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2843,12 +2843,10 @@ function strategy_smart_build($fun, $array) { } function strategy_sane($fun, $array) { global $config; - // Well, ideally a sane strategy would involve a more stringent checking, - // but let's at least have something to get the ball rolling :^) - if (php_sapi_name() == 'cli') return false; - else if (isset($_POST['mod']) || isset($_POST['json_response'])) return false; - else if ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1)) return array('immediate'); + else if (isset($_POST['mod'])) return false; + // Thread needs to be done instantly. Same with a board page, but only if posting a new thread. + else if ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1 && isset ($_POST['page']))) return array('immediate'); else return false; } From d285a796674f170b0e7a71ffa0cc7bbee2d5421a Mon Sep 17 00:00:00 2001 From: fatchan Date: Tue, 31 May 2016 23:28:55 +1000 Subject: [PATCH 141/212] Move the 'Go back and rebuild again' to the top of the rebuilt page so you dont have to scroll --- templates/mod/rebuilt.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/mod/rebuilt.html b/templates/mod/rebuilt.html index 57d16fff..f27cdf56 100644 --- a/templates/mod/rebuilt.html +++ b/templates/mod/rebuilt.html @@ -1,12 +1,12 @@

    {% trans 'Rebuilt' %}

    +

    + {% trans 'Go back and rebuild again' %}. +

      {% for log in logs %}
    • {{ log }}
    • {% endfor %}
    -

    - {% trans 'Go back and rebuild again' %}. -

    From 94c91db097c034c46f66b27d16eb84a4ea814bb6 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 9 Jun 2016 04:51:05 +0200 Subject: [PATCH 142/212] fix news deletion; thanks MrFreeman --- templates/mod/news.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/mod/news.html b/templates/mod/news.html index 510d60ce..57058291 100644 --- a/templates/mod/news.html +++ b/templates/mod/news.html @@ -40,7 +40,7 @@
    {% if mod|hasPermission(config.mod.news_delete) %} - [{% trans 'delete' %}] + [{% trans 'delete' %}] {% endif %}

    From 8a46c7a0d54864d7f640e1972a23e78018368cc2 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 9 Jun 2016 11:08:29 +0200 Subject: [PATCH 143/212] tesseract OCR support for spamfilters --- inc/config.php | 13 +++++++++++++ inc/functions.php | 2 +- post.php | 43 +++++++++++++++++++++++++++++++++++++----- tmp/tesseract/.gitkeep | 0 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tmp/tesseract/.gitkeep diff --git a/inc/config.php b/inc/config.php index 5926eb1d..ebe17572 100644 --- a/inc/config.php +++ b/inc/config.php @@ -824,6 +824,15 @@ // Set this to true if you're using Linux and you can execute `md5sum` binary. $config['gnu_md5'] = false; + // Use Tesseract OCR to retrieve text from images, so you can use it as a spamfilter. + $config['tesseract_ocr'] = false; + + // Tesseract parameters + $config['tesseract_params'] = ''; + + // Tesseract preprocess command + $config['tesseract_preprocess_command'] = 'convert -monochrome %s -'; + // Number of posts in a "View Last X Posts" page $config['noko50_count'] = 50; // Number of posts a thread needs before it gets a "View Last X Posts" page. @@ -1015,6 +1024,10 @@ // Minify Javascript using http://code.google.com/p/minify/. $config['minify_js'] = false; + // Dispatch thumbnail loading and image configuration with JavaScript. It will need a certain javascript + // code to work. + $config['javascript_image_dispatch'] = false; + /* * ==================== * Video embedding diff --git a/inc/functions.php b/inc/functions.php index 154386b0..5a8f6000 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2695,7 +2695,7 @@ function slugify($post) { elseif (isset ($post['body_nomarkup']) && $post['body_nomarkup']) $slug = $post['body_nomarkup']; elseif (isset ($post['body']) && $post['body']) - $slug = strip_html($post['body']); + $slug = strip_tags($post['body']); // Fix UTF-8 first $slug = mb_convert_encoding($slug, "UTF-8", "UTF-8"); diff --git a/post.php b/post.php index 4ea08ce3..bf723e01 100644 --- a/post.php +++ b/post.php @@ -652,14 +652,14 @@ if (isset($_POST['delete'])) { $post['filehash'] = md5($allhashes); } } - + if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) { - require_once 'inc/filters.php'; - + require_once 'inc/filters.php'; + do_filters($post); } - - if ($post['has_file']) { + + if ($post['has_file']) { foreach ($post['files'] as $key => &$file) { if ($file['is_an_image']) { if ($config['ie_mime_type_detection'] !== false) { @@ -787,6 +787,34 @@ if (isset($_POST['delete'])) { $file['thumbwidth'] = $size[0]; $file['thumbheight'] = $size[1]; } + + if ($config['tesseract_ocr']) { // Let's OCR it! + $fname = $file['tmp_name']; + + if ($file['height'] > 500 || $file['width'] > 500) { + $fname = $file['thumb']; + } + + if ($fname == 'spoiler') { // We don't have that much CPU time, do we? + } + else { + $tmpname = "tmp/tesseract/".rand(0,10000000); + + // Preprocess command is an ImageMagick b/w quantization + $error = shell_exec_error(sprintf($config['tesseract_preprocess_command'], escapeshellarg($fname)) . " | " . + 'tesseract stdin '.escapeshellarg($tmpname).' '.$config['tesseract_params']); + $tmpname .= ".txt"; + + $value = @file_get_contents($tmpname); + @unlink($tmpname); + + if ($value && trim($value)) { + // This one has an effect, that the body is appended to a post body. So you can write a correct + // spamfilter. + $post['body_nomarkup'] .= "".htmlspecialchars($value).""; + } + } + } if (!isset($dont_copy_file) || !$dont_copy_file) { if (isset($file['file_tmp'])) { @@ -827,6 +855,11 @@ if (isset($_POST['delete'])) { } } + // Do filters again if OCRing + if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri'])) { + do_filters($post); + } + if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) { undoImage($post); if ($config['robot_mute']) { diff --git a/tmp/tesseract/.gitkeep b/tmp/tesseract/.gitkeep new file mode 100644 index 00000000..e69de29b From d2bb4a776f681ad47a8b44bc271c8f0b502ef1f1 Mon Sep 17 00:00:00 2001 From: czaks Date: Tue, 17 May 2016 09:53:09 +0200 Subject: [PATCH 144/212] fail gracefully on no thumbnail --- inc/functions.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 5a8f6000..e576cf99 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1161,8 +1161,10 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) { foreach ($files as $i => $f) { if (($file !== false && $i == $file) || $file === null) { // Delete thumbnail - file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb); - unset($files[$i]->thumb); + if (isset ($f->thumb) && $f->thumb) { + file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb); + unset($files[$i]->thumb); + } // Delete file file_unlink($board['dir'] . $config['dir']['img'] . $f->file); From cdd963e79e0ffc5a7bcd69c9cd79d41c8ad74f52 Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 1 Jun 2016 00:20:20 +1000 Subject: [PATCH 145/212] fix flag spacing --- templates/post/flag.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/post/flag.html b/templates/post/flag.html index 61622368..481e40ae 100644 --- a/templates/post/flag.html +++ b/templates/post/flag.html @@ -1,5 +1,5 @@ {% if config.display_flags and post.modifiers.flag %} - Date: Wed, 1 Jun 2016 19:51:21 +1000 Subject: [PATCH 146/212] CSS hover instead of javascript mouseover. Need to use important because the dark/light text determined by IDToRGB is added inline and takes priority over stylesheets. --- js/id_colors.js | 6 ------ stylesheets/style.css | 4 +++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/js/id_colors.js b/js/id_colors.js index 6335f960..55454476 100644 --- a/js/id_colors.js +++ b/js/id_colors.js @@ -50,12 +50,6 @@ if (active_page == 'thread' || active_page == 'index') { "border-radius": "8px", "color": ft }); - - $(el).mouseover(function() { - $(this).css('color', '#800000'); // how about a CSS :hover rule instead? - }).mouseout(function() { - $(this).css('color', ft); - }); } $(".poster_id").each(function(k, v){ diff --git a/stylesheets/style.css b/stylesheets/style.css index 39d1fd04..7bedfe14 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -913,7 +913,9 @@ pre { .poster_id { cursor: pointer; } - +.poster_id:hover { + color: #800000!important; +} .poster_id::before { content: " ID: "; } From aa0d92a2b413e268ecf60211cf2132248c3c72ad Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 1 Jun 2016 20:35:17 +1000 Subject: [PATCH 147/212] Force post-hover.js to show OP's on hover. --- js/post-hover.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/js/post-hover.js b/js/post-hover.js index 0becfb70..780f8cff 100644 --- a/js/post-hover.js +++ b/js/post-hover.js @@ -54,10 +54,7 @@ onready(function(){ hovered_at = {'x': e.pageX, 'y': e.pageY}; var start_hover = function($link) { - if($.contains($post[0], $link[0])) { - // link links to itself or to op; ignore - } - else if($post.is(':visible') && + if ($post.is(':visible') && $post.offset().top >= $(window).scrollTop() && $post.offset().top + $post.height() <= $(window).scrollTop() + $(window).height()) { // post is in view From f27c26907d37c9d04f848f5594022a79a34624ee Mon Sep 17 00:00:00 2001 From: fatchan Date: Tue, 7 Jun 2016 23:20:38 +1000 Subject: [PATCH 148/212] Remove hardcoded 8chan links in catalog RSS --- templates/themes/catalog/index.rss | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/themes/catalog/index.rss b/templates/themes/catalog/index.rss index 248b494a..7add8869 100644 --- a/templates/themes/catalog/index.rss +++ b/templates/themes/catalog/index.rss @@ -2,17 +2,17 @@ /{{ board.uri }}/ - {{ board.title|e }} - https://8ch.net/{{ board.uri }}/ + {{ config.root }}{{ board.uri }}/ Live feed of new threads on the 8chan board /{{ board.uri }}/ - {{ board.title|e }}. - + {% for post in recent_posts %} {% if post.subject %}{{ post.subject|e }}{% else %}{{ post.body_nomarkup[:256]|remove_modifiers|e }}{% endif %} - https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html - https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html - https://8ch.net/{{ board.uri }}/res/{{ post.id }}.html + {{ config.root }}{{ board.uri }}/res/{{ post.id }}.html + {{ config.root }}{{ board.uri }}/res/{{ post.id }}.html + {{ config.root }}{{ board.uri }}/res/{{ post.id }}.html {{ post.pubdate }} - {{ post.body }} ]]> + {{ post.body }} ]]> {% endfor %} From 4f3cc7f316d18b159ed5aef3878a9d30d1717d4e Mon Sep 17 00:00:00 2001 From: fatchan Date: Tue, 7 Jun 2016 23:29:19 +1000 Subject: [PATCH 149/212] Whoops --- templates/themes/catalog/index.rss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/themes/catalog/index.rss b/templates/themes/catalog/index.rss index 7add8869..e6576ffc 100644 --- a/templates/themes/catalog/index.rss +++ b/templates/themes/catalog/index.rss @@ -3,7 +3,7 @@ /{{ board.uri }}/ - {{ board.title|e }} {{ config.root }}{{ board.uri }}/ - Live feed of new threads on the 8chan board /{{ board.uri }}/ - {{ board.title|e }}. + Live feed of new threads on the board /{{ board.uri }}/ - {{ board.title|e }}. {% for post in recent_posts %} From f23d11be60dd03039b04320dc0ea83f563332c9c Mon Sep 17 00:00:00 2001 From: Duane Moody Date: Thu, 22 Oct 2015 10:50:59 -0700 Subject: [PATCH 150/212] Prevent poster IDs from wordwrapping Poster IDs still linebreak between "ID:" and the ID, this corrects that without having to replace the space inbetween with an   nonbreaking space. --- stylesheets/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/stylesheets/style.css b/stylesheets/style.css index 7bedfe14..abd94973 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -912,6 +912,7 @@ pre { .poster_id { cursor: pointer; + white-space:nowrap; } .poster_id:hover { color: #800000!important; From c9ef21bff98698902cf5c89b3baf29a17864fd7a Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 8 Jun 2016 00:03:50 +1000 Subject: [PATCH 151/212] Better solution to prevent ID wrapping. Also no longer text-selectable. Much cleaner. --- stylesheets/style.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stylesheets/style.css b/stylesheets/style.css index abd94973..23d3ad7d 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -912,7 +912,12 @@ pre { .poster_id { cursor: pointer; - white-space:nowrap; + display: inline-block; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; } .poster_id:hover { color: #800000!important; From e230f1472cdd9fe5d416196d51e905eac22ce70e Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 10 Jun 2016 12:41:53 +0200 Subject: [PATCH 152/212] don`t ocr non-images --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index bf723e01..383154fb 100644 --- a/post.php +++ b/post.php @@ -788,7 +788,7 @@ if (isset($_POST['delete'])) { $file['thumbheight'] = $size[1]; } - if ($config['tesseract_ocr']) { // Let's OCR it! + if ($config['tesseract_ocr'] && $file['thumb'] != 'file') { // Let's OCR it! $fname = $file['tmp_name']; if ($file['height'] > 500 || $file['width'] > 500) { From 356f46237c3281b09d1fcc1df4e19cb12947063e Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 19 Jun 2016 02:15:49 +0200 Subject: [PATCH 153/212] fix install.sql after a bad merge --- install.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/install.sql b/install.sql index 720e4ed5..bf9b92bf 100644 --- a/install.sql +++ b/install.sql @@ -314,7 +314,6 @@ CREATE TABLE `pages` ( UNIQUE KEY `u_pages` (`name`,`board`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ->>>>>>> 12fa8ec... Edit static pages commit /*!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 */; From fed9065cf1fe759e20ec10baf0aba1309ee6f8ab Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 19 Jun 2016 02:23:24 +0200 Subject: [PATCH 154/212] skip non-image files in recent themes; fixes vichan-devel/vichan#185 --- templates/themes/recent/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/themes/recent/theme.php b/templates/themes/recent/theme.php index 95921c35..a826048f 100644 --- a/templates/themes/recent/theme.php +++ b/templates/themes/recent/theme.php @@ -65,7 +65,7 @@ if (isset($post['files'])) $files = json_decode($post['files']); - if ($files[0]->file == 'deleted') continue; + if ($files[0]->file == 'deleted' || $files[0]->thumb == 'file') continue; // board settings won't be available in the template file, so generate links now $post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] From 8f4aa2732900afc837d5154f518dc3a683f58bd1 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 19 Jun 2016 02:40:24 +0200 Subject: [PATCH 155/212] fix file-selector.js dependencies --- js/file-selector.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/file-selector.js b/js/file-selector.js index 207a5ae9..c2b5381b 100644 --- a/js/file-selector.js +++ b/js/file-selector.js @@ -1,8 +1,9 @@ /* - * file-selector.js - Add support for drag and drop file selection, and paste from clipbboard on supported browsers. + * file-selector.js - Add support for drag and drop file selection, and paste from clipboard on supported browsers. * * Usage: * $config['additional_javascript'][] = 'js/jquery.min.js'; + * $config['additional_javascript'][] = 'js/ajax.js'; * $config['additional_javascript'][] = 'js/file-selector.js'; */ function init_file_selector(max_images) { From 11cecf8452d724b1a61e82292cff1cc16a5403e7 Mon Sep 17 00:00:00 2001 From: czaks Date: Tue, 21 Jun 2016 05:03:44 +0200 Subject: [PATCH 156/212] Revert "[BUG] Image reject repost board option now also affects YT embeds" This reverts commit b476b660073adbe827836a7e7860b0fb3a061ff0. --- inc/functions.php | 47 ----------------------------------------------- post.php | 28 +--------------------------- 2 files changed, 1 insertion(+), 74 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index e576cf99..043d4de7 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -2492,53 +2492,6 @@ function getPostByHashInThread($hash, $thread) { return false; } -function getPostByEmbed($embed) { - global $board, $config; - $matches = array(); - foreach ($config['embedding'] as &$e) { - if (preg_match($e[0], $embed, $matches) && isset($matches[1]) && !empty($matches[1])) { - $embed = '%'.$matches[1].'%'; - break; - } - } - - if (!isset($embed)) return false; - - $query = prepare(sprintf("SELECT `id`,`thread` FROM ``posts_%s`` WHERE `embed` LIKE :embed", $board['uri'])); - $query->bindValue(':embed', $embed, PDO::PARAM_STR); - $query->execute() or error(db_error($query)); - - if ($post = $query->fetch(PDO::FETCH_ASSOC)) { - return $post; - } - - return false; -} - -function getPostByEmbedInThread($embed, $thread) { - global $board, $config; - $matches = array(); - foreach ($config['embedding'] as &$e) { - if (preg_match($e[0], $embed, $matches) && isset($matches[1]) && !empty($matches[1])) { - $embed = '%'.$matches[1].'%'; - break; - } - } - - if (!isset($embed)) return false; - - $query = prepare(sprintf("SELECT `id`,`thread` FROM ``posts_%s`` WHERE `embed` = :embed AND ( `thread` = :thread OR `id` = :thread )", $board['uri'])); - $query->bindValue(':embed', $embed, PDO::PARAM_STR); - $query->bindValue(':thread', $thread, PDO::PARAM_INT); - $query->execute() or error(db_error($query)); - - if ($post = $query->fetch(PDO::FETCH_ASSOC)) { - return $post; - } - - return false; -} - function undoImage(array $post) { if (!$post['has_file'] || !isset($post['files'])) return; diff --git a/post.php b/post.php index 383154fb..aa1b8235 100644 --- a/post.php +++ b/post.php @@ -295,32 +295,6 @@ if (isset($_POST['delete'])) { if (!isset($post['embed'])) { error($config['error']['invalid_embed']); } - - if ($config['image_reject_repost']) { - if ($p = getPostByEmbed($post['embed'])) { - error(sprintf($config['error']['fileexists'], - ($post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root']) . - ($board['dir'] . $config['dir']['res'] . - ($p['thread'] ? - $p['thread'] . '.html#' . $p['id'] - : - $p['id'] . '.html' - )) - )); - } - } else if (!$post['op'] && $config['image_reject_repost_in_thread']) { - if ($p = getPostByEmbedInThread($post['embed'], $post['thread'])) { - error(sprintf($config['error']['fileexistsinthread'], - ($post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root']) . - ($board['dir'] . $config['dir']['res'] . - ($p['thread'] ? - $p['thread'] . '.html#' . $p['id'] - : - $p['id'] . '.html' - )) - )); - } - } } if (!hasPermission($config['mod']['bypass_field_disable'], $board['uri'])) { @@ -853,7 +827,7 @@ if (isset($_POST['delete'])) { )); } } - } + } // Do filters again if OCRing if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri'])) { From a55760299c6777fc841a9990fa9b590b17663bb8 Mon Sep 17 00:00:00 2001 From: nekomiko482 Date: Thu, 7 Jul 2016 12:53:40 +0300 Subject: [PATCH 157/212] Fixes incompatibility with BSD's md5 output format. fixes #190 --- post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post.php b/post.php index aa1b8235..753d2006 100644 --- a/post.php +++ b/post.php @@ -607,7 +607,7 @@ if (isset($_POST['delete'])) { error($config['error']['nomove']); if ($md5cmd) { - $output = shell_exec_error($md5cmd . " < " . escapeshellarg($upload)); + $output = shell_exec_error($md5cmd . " " . escapeshellarg($upload)); $output = explode(' ', $output); $hash = $output[0]; } From 8548a4ff703acd5a77e4ce678eeb88e4261a1aa6 Mon Sep 17 00:00:00 2001 From: ptchan-foss Date: Fri, 12 Aug 2016 18:18:54 +0100 Subject: [PATCH 158/212] Fixed report syslog message --- post.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/post.php b/post.php index 753d2006..0f58dd4c 100644 --- a/post.php +++ b/post.php @@ -148,15 +148,15 @@ if (isset($_POST['delete'])) { markup($reason); foreach ($report as &$id) { - $query = prepare(sprintf("SELECT `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); + $query = prepare(sprintf("SELECT `id`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); - $thread = $query->fetchColumn(); + $post = $query->fetch(PDO::FETCH_ASSOC); if ($config['syslog']) _syslog(LOG_INFO, 'Reported post: ' . - '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($thread ? '#' . $id : '') . + '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') . ' for "' . $reason . '"' ); $query = prepare("INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason)"); From 5e335a8564a76f0449d47f0767b4baa8d2cb9e91 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 14 Aug 2016 16:24:17 +0200 Subject: [PATCH 159/212] preliminary inbound nntpchan support --- inc/config.php | 17 +++ install.php | 15 +- install.sql | 19 +++ post.php | 367 +++++++++++++++++++++++++++++++++++++------------ 4 files changed, 326 insertions(+), 92 deletions(-) diff --git a/inc/config.php b/inc/config.php index ebe17572..a8afdf7a 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1689,6 +1689,23 @@ // Example: Adding the pre-markup post body to the API as "com_nomarkup". // $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup'); +/* + * ================== + * NNTPChan settings + * ================== + */ + + $config['nntpchan'] = array(); + + // Enable NNTPChan integration + $config['nntpchan']['enabled'] = false; + + // NNTP server + $config['nntpchan']['server'] = "localhost:1119"; + + // Global dispatch array. Add your boards to it to enable them. + $config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test' + /* * ==================== * Other/uncategorized diff --git a/install.php b/install.php index de4d3dd0..6f1617a8 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. diff --git a/install.sql b/install.sql index bf9b92bf..3b390645 100644 --- a/install.sql +++ b/install.sql @@ -314,6 +314,25 @@ CREATE TABLE `pages` ( UNIQUE KEY `u_pages` (`name`,`board`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +-- -------------------------------------------------------- + +-- +-- Table structure for table `nntp_references` +-- + +CREATE TABLE `nntp_references` ( + `board` varchar(60) NOT NULL, + `id` int(11) unsigned NOT NULL, + `message_id` varchar(255) CHARACTER SET ascii NOT NULL, + `message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL, + `own` tinyint(1) NOT NULL, + `headers` text, + PRIMARY KEY (`message_id_digest`), + UNIQUE KEY `message_id` (`message_id`), + UNIQUE KEY `u_board_id` (`board`, `id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + + /*!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 aa1b8235..4af09928 100644 --- a/post.php +++ b/post.php @@ -11,6 +11,160 @@ if ((!isset($_POST['mod']) || !$_POST['mod']) && $config['board_locked']) { error("Board is locked"); } +$dropped_post = false; + +if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) { + $_POST = array(); + $_POST['json_response'] = true; + + $headers = json_encode($_GET); + + if (!isset ($_GET['Message-Id'])) { + if (!isset ($_GET['Message-ID'])) { + error("NNTPChan: No message ID"); + } + else $msgid = $_GET['Message-ID']; + } + else $msgid = $_GET['Message-Id']; + + $groups = preg_split("/,\s*/", $_GET['Newsgroups']); + if (count($groups) != 1) { + error("NNTPChan: Messages can go to only one newsgroup"); + } + $group = $groups[0]; + + if (!isset($config['nntpchan']['dispatch'][$group])) { + error("NNTPChan: We don't synchronize $group"); + } + $xboard = $config['nntpchan']['dispatch'][$group]; + + $ref = null; + if (isset ($_GET['References'])) { + $refs = preg_split("/,\s*/", $_GET['References']); + + if (count($refs) > 1) { + error("NNTPChan: We don't support multiple references"); + } + + $ref = $refs[0]; + + $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref"); + $query->bindValue(':ref', $ref); + $query->execute() or error(db_error($query)); + + $ary = $query->fetchAll(PDO::FETCH_ASSOC); + + if (count($ary) == 0) { + error("NNTPChan: We don't have $ref that $msgid references"); + } + + $p_id = $ary[0]['id']; + $p_board = $ary[0]['board']; + + if ($p_board != $xboard) { + error("NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard"); + } + + $_POST['thread'] = $p_id; + } + + $date = isset($_GET['Date']) ? strtotime($_GET['Date']) : time(); + + list($ct) = explode('; ', $_GET['Content-Type']); + + $query = prepare("SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid"); + $query->bindValue(":msgid", $msgid); + $query->execute() or error(db_error($query)); + + $a = $query->fetch(PDO::FETCH_ASSOC); + if ($a['c'] > 0) { + error("NNTPChan: We already have this post. Post discarded."); + } + + if ($ct == 'text/plain') { + $content = file_get_contents("php://input"); + } + elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') { + _syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); + $content = ''; + + $tmpfiles = $_FILES['attachment']; + foreach ($tmpfiles as $id => $file) { + if ($file['type'] == 'text/plain') { + $content .= file_get_contents($file['tmp_name']); + unset($_FILES['attachment'][$id]); + } + elseif ($file['type'] == 'message/rfc822') { // Signed message, ignore for now + unset($_FILES['attachment'][$id]); + } + else { // A real attachment :^) + } + } + + $_FILES = $_FILES['attachment']; + + + } + else { + error("NNTPChan: Wrong mime type: $ct"); + } + + $_POST['subject'] = isset($_GET['Subject']) ? $_GET['Subject'] : ''; + $_POST['board'] = $xboard; + + if (isset ($_GET['From'])) { + list($name, $mail) = explode(" <", $_GET['From'], 2); + $mail = preg_replace('/>\s+$/', '', $mail); + + $_POST['name'] = $name; + //$_POST['email'] = $mail; + $_POST['email'] = ''; + } + + if (isset ($_GET['X_Sage'])) { + $_POST['email'] = 'sage'; + } + + $content = preg_replace_callback('/>>([0-9a-fA-F]{6,})/', function($id) use ($xboard) { + $id = $id[1]; + + $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule"); + $idx = $id . "%"; + $query->bindValue(':rule', $idx); + $query->execute() or error(db_error($query)); + + $ary = $query->fetchAll(PDO::FETCH_ASSOC); + if (count($ary) == 0) { + return ">>>>$id"; + } + else { + $ret = array(); + foreach ($ary as $v) { + if ($v['board'] != $xboard) { + $ret[] = ">>>/".$v['board']."/".$v['id']; + } + else { + $ret[] = ">>".$v['id']; + } + } + return implode($ret, ", "); + } + }, $content); + + $_POST['body'] = $content; + + $dropped_post = array( + 'date' => $date, + 'board' => $xboard, + 'msgid' => $msgid, + 'headers' => $headers, + 'from_nntp' => true, + ); +} +elseif (isset($_GET['Newsgroups'])) { + error("NNTPChan: NNTPChan support is disabled"); +} + if (isset($_POST['delete'])) { // Delete @@ -178,8 +332,8 @@ if (isset($_POST['delete'])) { header('Content-Type: text/json'); echo json_encode(array('success' => true)); } -} elseif (isset($_POST['post'])) { - if (!isset($_POST['body'], $_POST['board'])) +} elseif (isset($_POST['post']) || $dropped_post) { + if (!isset($_POST['body'], $_POST['board']) && !$dropped_post) error($config['error']['bot']); $post = array('board' => $_POST['board'], 'files' => array()); @@ -206,61 +360,67 @@ if (isset($_POST['delete'])) { } else $post['op'] = true; - // Check for CAPTCHA right after opening the board so the "return" link is in there - if ($config['recaptcha']) { - if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) + + if (!$dropped_post) { + // Check for CAPTCHA right after opening the board so the "return" link is in there + if ($config['recaptcha']) { + if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) + error($config['error']['bot']); + // Check what reCAPTCHA has to say... + $resp = recaptcha_check_answer($config['recaptcha_private'], + $_SERVER['REMOTE_ADDR'], + $_POST['recaptcha_challenge_field'], + $_POST['recaptcha_response_field']); + if (!$resp->is_valid) { + error($config['error']['captcha']); + } + } + + if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || + (!$post['op'] && $_POST['post'] == $config['button_reply']))) error($config['error']['bot']); - // Check what reCAPTCHA has to say... - $resp = recaptcha_check_answer($config['recaptcha_private'], - $_SERVER['REMOTE_ADDR'], - $_POST['recaptcha_challenge_field'], - $_POST['recaptcha_response_field']); - if (!$resp->is_valid) { - error($config['error']['captcha']); - } - } - - if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || - (!$post['op'] && $_POST['post'] == $config['button_reply']))) - error($config['error']['bot']); - // Check the referrer - if ($config['referer_match'] !== false && - (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) - error($config['error']['referer']); + // Check the referrer + if ($config['referer_match'] !== false && + (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) + error($config['error']['referer']); - checkDNSBL(); + checkDNSBL(); - // Check if banned - checkBan($board['uri']); + // Check if banned + checkBan($board['uri']); - if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) { - check_login(false); - if (!$mod) { - // Liar. You're not a mod. - error($config['error']['notamod']); + if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) { + check_login(false); + if (!$mod) { + // Liar. You're not a mod. + error($config['error']['notamod']); + } + + $post['sticky'] = $post['op'] && isset($_POST['sticky']); + $post['locked'] = $post['op'] && isset($_POST['lock']); + $post['raw'] = isset($_POST['raw']); + + if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri'])) + error($config['error']['noaccess']); + if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri'])) + error($config['error']['noaccess']); + if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri'])) + error($config['error']['noaccess']); } - $post['sticky'] = $post['op'] && isset($_POST['sticky']); - $post['locked'] = $post['op'] && isset($_POST['lock']); - $post['raw'] = isset($_POST['raw']); - - if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri'])) - error($config['error']['noaccess']); - if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri'])) - error($config['error']['noaccess']); - if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri'])) - error($config['error']['noaccess']); - } + if (!$post['mod']) { + $post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null))); + if ($post['antispam_hash'] === true) + error($config['error']['spam']); + } - if (!$post['mod']) { - $post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null))); - if ($post['antispam_hash'] === true) - error($config['error']['spam']); + if ($config['robot_enable'] && $config['robot_mute']) { + checkMute(); + } } - - if ($config['robot_enable'] && $config['robot_mute']) { - checkMute(); + else { + $mod = $post['mod'] = false; } //Check if thread exists @@ -371,28 +531,36 @@ if (isset($_POST['delete'])) { $post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email'])); $post['body'] = $_POST['body']; $post['password'] = $_POST['password']; - $post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || !empty($_FILES['file']['name']))); + $post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || count($_FILES) > 0)); - if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) { - $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']); - if ($stripped_whitespace == '') { - error($config['error']['tooshort_body']); + if (!$dropped_post) { + + if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) { + $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']); + if ($stripped_whitespace == '') { + error($config['error']['tooshort_body']); + } + } + + if (!$post['op']) { + // Check if thread is locked + // but allow mods to post + if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) + error($config['error']['locked']); + + $numposts = numPosts($post['thread']); + + if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $numposts['replies']) + error($config['error']['reply_hard_limit']); + + if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images']) + error($config['error']['image_hard_limit']); } } - - if (!$post['op']) { - // Check if thread is locked - // but allow mods to post - if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) - error($config['error']['locked']); - - $numposts = numPosts($post['thread']); - - if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $numposts['replies']) - error($config['error']['reply_hard_limit']); - - if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images']) - error($config['error']['image_hard_limit']); + else { + if (!$post['op']) { + $numposts = numPosts($post['thread']); + } } if ($post['has_file']) { @@ -442,7 +610,7 @@ if (isset($_POST['delete'])) { $trip = generate_tripcode($post['name']); $post['name'] = $trip[0]; - $post['trip'] = isset($trip[1]) ? $trip[1] : ''; + $post['trip'] = isset($trip[1]) ? $trip[1] : ''; // XX: Dropped posts and tripcodes $noko = false; if (strtolower($post['email']) == 'noko') { @@ -477,15 +645,17 @@ if (isset($_POST['delete'])) { if (empty($post['files'])) $post['has_file'] = false; - // Check for a file - if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) { - if (!$post['has_file'] && $config['force_image_op']) - error($config['error']['noimage']); - } + if (!$dropped_post) { + // Check for a file + if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) { + if (!$post['has_file'] && $config['force_image_op']) + error($config['error']['noimage']); + } - // Check for too many files - if (sizeof($post['files']) > $config['max_images']) - error($config['error']['toomanyimages']); + // Check for too many files + if (sizeof($post['files']) > $config['max_images']) + error($config['error']['toomanyimages']); + } if ($config['strip_combining_chars']) { $post['name'] = strip_combining_chars($post['name']); @@ -494,18 +664,19 @@ if (isset($_POST['delete'])) { $post['body'] = strip_combining_chars($post['body']); } - // Check string lengths - if (mb_strlen($post['name']) > 35) - error(sprintf($config['error']['toolong'], 'name')); - if (mb_strlen($post['email']) > 40) - error(sprintf($config['error']['toolong'], 'email')); - if (mb_strlen($post['subject']) > 100) - error(sprintf($config['error']['toolong'], 'subject')); - if (!$mod && mb_strlen($post['body']) > $config['max_body']) - error($config['error']['toolong_body']); - if (mb_strlen($post['password']) > 20) - error(sprintf($config['error']['toolong'], 'password')); - + if (!$dropped_post) { + // Check string lengths + if (mb_strlen($post['name']) > 35) + error(sprintf($config['error']['toolong'], 'name')); + if (mb_strlen($post['email']) > 40) + error(sprintf($config['error']['toolong'], 'email')); + if (mb_strlen($post['subject']) > 100) + error(sprintf($config['error']['toolong'], 'subject')); + if (!$mod && mb_strlen($post['body']) > $config['max_body']) + error($config['error']['toolong_body']); + if (mb_strlen($post['password']) > 20) + error(sprintf($config['error']['toolong'], 'password')); + } wordfilters($post['body']); $post['body'] = escape_markup_modifiers($post['body']); @@ -514,6 +685,7 @@ if (isset($_POST['delete'])) { $post['body'] .= "\n1"; } + if (!$dropped_post) if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) { require 'inc/lib/geoip/geoip.inc'; $gi=geoip\geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD); @@ -555,6 +727,7 @@ if (isset($_POST['delete'])) { $post['body'] .= "\n" . $_POST['tag'] . ""; } + if (!$dropped_post) if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']); $post['body'] .= "\n".$proxy.""; @@ -627,7 +800,7 @@ if (isset($_POST['delete'])) { } } - if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) { + if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) { require_once 'inc/filters.php'; do_filters($post); @@ -830,11 +1003,11 @@ if (isset($_POST['delete'])) { } // Do filters again if OCRing - if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri'])) { + if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) { do_filters($post); } - if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) { + if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) { undoImage($post); if ($config['robot_mute']) { error(sprintf($config['error']['muted'], mute())); @@ -873,6 +1046,18 @@ if (isset($_POST['delete'])) { $post['id'] = $id = post($post); $post['slug'] = slugify($post); + + if ($dropped_post && $dropped_post['from_nntp']) { + $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ". + "(:board , :id , :message_id , :message_id_digest , false, :headers)"); + + $query->bindValue(':board', $dropped_post['board']); + $query->bindValue(':id', $id); + $query->bindValue(':message_id', $dropped_post['msgid']); + $query->bindValue(':message_id_digest', sha1($dropped_post['msgid'])); + $query->bindValue(':headers', $dropped_post['headers']); + $query->execute() or error(db_error($query)); + } insertFloodPost($post); // Handle cyclical threads From a779b96370d3353c8409d64b70c724fa783ad219 Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 15 Aug 2016 00:56:06 +0200 Subject: [PATCH 160/212] second iteration of nntpchan implementation --- inc/config.php | 24 +++++- inc/nntpchan/nntpchan.php | 149 ++++++++++++++++++++++++++++++++++++++ inc/nntpchan/tests.php | 30 ++++++++ post.php | 29 ++++++++ 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 inc/nntpchan/nntpchan.php create mode 100644 inc/nntpchan/tests.php diff --git a/inc/config.php b/inc/config.php index a8afdf7a..bfb774a8 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1693,6 +1693,11 @@ * ================== * NNTPChan settings * ================== + */ + +/* + * Please keep in mind that NNTPChan support in vichan isn't finished yet / is in an experimental + * state. Please join #nntpchan on Rizon in order to peer with someone. */ $config['nntpchan'] = array(); @@ -1703,9 +1708,26 @@ // NNTP server $config['nntpchan']['server'] = "localhost:1119"; - // Global dispatch array. Add your boards to it to enable them. + // Global dispatch array. Add your boards to it to enable them. Please make + // sure that this setting is set in a global context. $config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test' + // Trusted peer - an IP address of your NNTPChan instance. This peer will have + // increased capabilities, eg.: will evade spamfilter. + $config['nntpchan']['trusted_peer'] = '127.0.0.1'; + + // Salt for message ID generation. Keep it long and secure. + $config['nntpchan']['salt'] = 'change_me+please'; + + // A local message ID domain. Make sure to change it. + $config['nntpchan']['domain'] = 'example.vichan.net'; + + // An NNTPChan group name. + // Please set this setting in your board/config.php, not globally. + $config['nntpchan']['group'] = false; // eg. 'overchan.test' + + + /* * ==================== * Other/uncategorized diff --git a/inc/nntpchan/nntpchan.php b/inc/nntpchan/nntpchan.php new file mode 100644 index 00000000..25215e94 --- /dev/null +++ b/inc/nntpchan/nntpchan.php @@ -0,0 +1,149 @@ +"; +} + + +function gen_nntp($headers, $files) { + if (count($files) == 0) { + } + else if (count($files) == 1 && $files[0]['type'] == 'text/plain') { + $content = $files[0]['text'] . "\r\n"; + $headers['Content-Type'] = "text/plain; charset=UTF-8"; + } + else { + $boundary = sha1($headers['Message-Id']); + $content = ""; + $headers['Content-Type'] = "multipart/mixed; boundary=$boundary"; + foreach ($files as $file) { + $content .= "--$boundary\r\n"; + if (isset($file['name'])) { + $file['name'] = preg_replace('/[\r\n\0"]/', '', $file['name']); + $content .= "Content-Disposition: form-data; filename=\"$file[name]\"; name=\"attachment\"\r\n"; + } + $type = explode('/', $file['type'])[0]; + if ($type == 'text') { + $file['type'] .= '; charset=UTF-8'; + } + $content .= "Content-Type: $file[type]\r\n"; + if ($type != 'text' && $type != 'message') { + $file['text'] = base64_encode($file['text']); + $content .= "Content-Transfer-Encoding: base64\r\n"; + } + $content .= "\r\n"; + $content .= $file['text']; + $content .= "\r\n"; + } + $content .= "--$boundary--\r\n"; + + $headers['Mime-Version'] = '1.0'; + } + //$headers['Content-Length'] = strlen($content); + $headers['Date'] = date('r', $headers['Date']); + $out = ""; + foreach ($headers as $id => $val) { + $val = str_replace("\n", "\n\t", $val); + $out .= "$id: $val\r\n"; + } + $out .= "\r\n"; + $out .= $content; + return $out; +} + +function nntp_publish($msg, $id) { + $s = fsockopen("tcp://localhost:1119"); + fgets($s); + fputs($s, "MODE STREAM\r\n"); + fgets($s); + fputs($s, "TAKETHIS $id\r\n"); + fputs($s, $msg); + fputs($s, "\r\n.\r\n"); + fgets($s); + fclose($s); +} + +function post2nntp($post, $msgid) { + global $config; + + $headers = array(); + $files = array(); + + $headers['Message-Id'] = $msgid; + $headers['Newsgroups'] = $config['nntpchan']['group']; + $headers['Date'] = time(); + $headers['Subject'] = $post['subject'] ? $post['subject'] : "None"; + $headers['From'] = $post['name'] . " "; + + if ($post['email'] == 'sage') { + $headers['X-Sage'] = true; + } + + if (!$post['op']) { + // Get muh parent + $query = prepare("SELECT `message_id` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id"); + $query->bindValue(':board', $post['board']); + $query->bindValue(':id', $post['thread']); + $query->execute() or error(db_error($query)); + + if ($result = $query->fetch(PDO::FETCH_ASSOC)) { + $headers['References'] = $result['message_id']; + } + else { + return false; // We don't have OP. Discarding. + } + } + + // Let's parse the body a bit. + $body = trim($post['body_nomarkup']); + $body = preg_replace('/\r?\n/', "\r\n", $body); + $body = preg_replace_callback('@>>(>/([a-zA-Z0-9_+-]+)/)?([0-9]+)@', function($o) use ($post) { + if ($o[1]) { + $board = $o[2]; + } + else { + $board = $post['board']; + } + $id = $o[3]; + + $query = prepare("SELECT `message_id_digest` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id"); + $query->bindValue(':board', $board); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + + if ($result = $query->fetch(PDO::FETCH_ASSOC)) { + return ">>".substr($result['message_id_digest'], 0, 16); + } + else { + return $o[0]; // Should send URL imo + } + }, $body); + $body = preg_replace('/>>>>([0-9a-fA-F])+/', '>>\1', $body); + + + $files[] = array('type' => 'text/plain', 'text' => $body); + + foreach ($post['files'] as $id => $file) { + $fc = array(); + + $fc['type'] = $file['type']; + $fc['text'] = file_get_contents($file['file_path']); + $fc['name'] = $file['name']; + + $files[] = $fc; + } + + return array($headers, $files); +} diff --git a/inc/nntpchan/tests.php b/inc/nntpchan/tests.php new file mode 100644 index 00000000..a63789d7 --- /dev/null +++ b/inc/nntpchan/tests.php @@ -0,0 +1,30 @@ + "czaks ", "Message-Id" => "<1234.0000.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None"], +[['type' => 'text/plain', 'text' => "THIS IS A NEW TEST THREAD"]]); +echo "\n@@@@ Single msg:\n"; +echo $m1 = gennntp(["From" => "czaks ", "Message-Id" => "<1234.1234.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"], +[['type' => 'text/plain', 'text' => "hello world, with no image :("]]); +echo "\n@@@@ Single msg and pseudoimage:\n"; +echo $m2 = gennntp(["From" => "czaks ", "Message-Id" => "<1234.2137.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"], +[['type' => 'text/plain', 'text' => "hello world, now with an image!"], + ['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"]]); +echo "\n@@@@ Single msg and two pseudoimages:\n"; +echo $m3 = gennntp(["From" => "czaks ", "Message-Id" => "<1234.1488.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"], +[['type' => 'text/plain', 'text' => "hello world, now WITH TWO IMAGES!!!"], + ['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"], + ['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif2.gif"]]); +shoveitup($m0, "<1234.0000.".$time."@example.vichan.net>"); +sleep(1); +shoveitup($m1, "<1234.1234.".$time."@example.vichan.net>"); +sleep(1); +shoveitup($m2, "<1234.2137.".$time."@example.vichan.net>"); +shoveitup($m3, "<1234.1488.".$time."@example.vichan.net>"); + diff --git a/post.php b/post.php index 4590a534..1e4edcee 100644 --- a/post.php +++ b/post.php @@ -13,7 +13,12 @@ if ((!isset($_POST['mod']) || !$_POST['mod']) && $config['board_locked']) { $dropped_post = false; +// Is it a post coming from NNTP? Let's extract it and pretend it's a normal post. if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) { + if ($_SERVER['REMOTE_ADDR'] != $config['nntpchan']['trusted_peer']) { + error("NNTPChan: Forbidden. $_SERVER[REMOTE_ADDR] is not a trusted peer"); + } + $_POST = array(); $_POST['json_response'] = true; @@ -1057,7 +1062,31 @@ if (isset($_POST['delete'])) { $query->bindValue(':message_id_digest', sha1($dropped_post['msgid'])); $query->bindValue(':headers', $dropped_post['headers']); $query->execute() or error(db_error($query)); + } // ^^^^^ For inbound posts ^^^^^ + elseif ($config['nntpchan']['enabled'] && $config['nntpchan']['group']) { + // vvvvv For outbound posts vvvvv + + require_once('inc/nntpchan/nntpchan.php'); + $msgid = gen_msgid($post['board'], $post['id']); + + list($headers, $files) = post2nntp($post, $msgid); + + $message = gen_nntp($headers, $files); + + $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ". + "(:board , :id , :message_id , :message_id_digest , true , :headers)"); + + $query->bindValue(':board', $post['board']); + $query->bindValue(':id', $post['id']); + $query->bindValue(':message_id', $msgid); + $query->bindValue(':message_id_digest', sha1($msgid)); + $query->bindValue(':headers', json_encode($headers)); + $query->execute() or error(db_error($query)); + + // Let's broadcast it! + nntp_publish($message, $msgid); } + insertFloodPost($post); // Handle cyclical threads From 0b19051891e3a45a913cef38f0921cf781a0817b Mon Sep 17 00:00:00 2001 From: czaks Date: Mon, 15 Aug 2016 04:13:26 +0200 Subject: [PATCH 161/212] fix a notice; increase waiting time for dns --- inc/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 043d4de7..1b336e12 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -892,7 +892,7 @@ function displayBan($ban) { Element('page.html', array( 'title' => _('Banned!'), 'config' => $config, - 'boardlist' => createBoardlist($mod), + 'boardlist' => createBoardlist(isset($mod) ? $mod : false), 'body' => Element('banned.html', array( 'config' => $config, 'ban' => $ban, @@ -2514,7 +2514,7 @@ function rDNS($ip_addr) { if (!$config['dns_system']) { $host = gethostbyaddr($ip_addr); } else { - $resp = shell_exec_error('host -W 1 ' . $ip_addr); + $resp = shell_exec_error('host -W 3 ' . $ip_addr); if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m)) $host = $m[1]; else From 1c3e6e590aca050802f271d157f0d274c8217d4d Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 19 Aug 2016 16:05:50 -0400 Subject: [PATCH 162/212] patch for nntpchan stream --- inc/nntpchan/nntpchan.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/inc/nntpchan/nntpchan.php b/inc/nntpchan/nntpchan.php index 25215e94..f21fdd2d 100644 --- a/inc/nntpchan/nntpchan.php +++ b/inc/nntpchan/nntpchan.php @@ -64,7 +64,9 @@ function gen_nntp($headers, $files) { } function nntp_publish($msg, $id) { - $s = fsockopen("tcp://localhost:1119"); + global $config; + $server = $config["nntpchan"]["server"]; + $s = fsockopen("tcp://$server"); fgets($s); fputs($s, "MODE STREAM\r\n"); fgets($s); @@ -72,6 +74,7 @@ function nntp_publish($msg, $id) { fputs($s, $msg); fputs($s, "\r\n.\r\n"); fgets($s); + fputs($s, "QUIT\r\n"); fclose($s); } From a5e7b3da6f8ee764ab831d3c1b2df6c17a5e78ca Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 19 Aug 2016 23:15:08 +0200 Subject: [PATCH 163/212] nntpchan: work around php nonsense --- inc/nntpchan/nntpchan.php | 2 +- post.php | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/inc/nntpchan/nntpchan.php b/inc/nntpchan/nntpchan.php index 25215e94..8a2fba44 100644 --- a/inc/nntpchan/nntpchan.php +++ b/inc/nntpchan/nntpchan.php @@ -124,7 +124,7 @@ function post2nntp($post, $msgid) { $query->execute() or error(db_error($query)); if ($result = $query->fetch(PDO::FETCH_ASSOC)) { - return ">>".substr($result['message_id_digest'], 0, 16); + return ">>".substr($result['message_id_digest'], 0, 18); } else { return $o[0]; // Should send URL imo diff --git a/post.php b/post.php index 1e4edcee..47c5f0ef 100644 --- a/post.php +++ b/post.php @@ -90,31 +90,36 @@ if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) { $content = file_get_contents("php://input"); } elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') { - _syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); + _syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); // Debug + $content = ''; - $tmpfiles = $_FILES['attachment']; - foreach ($tmpfiles as $id => $file) { - if ($file['type'] == 'text/plain') { - $content .= file_get_contents($file['tmp_name']); - unset($_FILES['attachment'][$id]); + $newfiles = array(); + foreach ($_FILES['attachment']['error'] as $id => $error) { + if ($_FILES['attachment']['type'][$id] == 'text/plain') { + $content .= file_get_contents($_FILES['attachment']['tmp_name'][$id]); } - elseif ($file['type'] == 'message/rfc822') { // Signed message, ignore for now - unset($_FILES['attachment'][$id]); + elseif ($_FILES['attachment']['type'][$id] == 'message/rfc822') { // Signed message, ignore for now } else { // A real attachment :^) + $file = array(); + $file['name'] = $_FILES['attachment']['name'][$id]; + $file['type'] = $_FILES['attachment']['type'][$id]; + $file['size'] = $_FILES['attachment']['size'][$id]; + $file['tmp_name'] = $_FILES['attachment']['tmp_name'][$id]; + $file['error'] = $_FILES['attachment']['error'][$id]; + + $newfiles["file$id"] = $file; } } - $_FILES = $_FILES['attachment']; - - + $_FILES = $newfiles; } else { error("NNTPChan: Wrong mime type: $ct"); } - $_POST['subject'] = isset($_GET['Subject']) ? $_GET['Subject'] : ''; + $_POST['subject'] = isset($_GET['Subject']) ? ($_GET['Subject'] == 'None' ? '' : $_GET['Subject']) : ''; $_POST['board'] = $xboard; if (isset ($_GET['From'])) { From 8951cb74c8fbface07e38308994bc2fe480e3c82 Mon Sep 17 00:00:00 2001 From: "Michael D. Reiley" Date: Thu, 22 Sep 2016 23:03:11 -0700 Subject: [PATCH 164/212] Rebuild index when mod deletes a thread. The index does not properly rebuild when a mod deletes a thread, resulting in a ghost thread remaining in the index until the next rebuild. This fix was originally contributed to Uboachan's codebase by Mannosuke. --- inc/mod/pages.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 303fa3dd..c3e98af5 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1721,6 +1721,8 @@ function mod_deletebyip($boardName, $post, $global = false) { deletePost($post['id'], false, false); rebuildThemes('post-delete', $board['uri']); + + buildIndex(); if ($post['thread']) $threads_to_rebuild[$post['board']][$post['thread']] = true; From 372c26491ae059ca4dd39cba3446f98e4e590698 Mon Sep 17 00:00:00 2001 From: "Michael D. Reiley" Date: Sat, 1 Oct 2016 15:06:09 -0700 Subject: [PATCH 165/212] Fix typo in max_images comment multi_image.js should be multi-image.js, with a dash, not an underscore. --- inc/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index bfb774a8..6d1a8081 100644 --- a/inc/config.php +++ b/inc/config.php @@ -664,7 +664,7 @@ */ // Maximum number of images allowed. Increasing this number enabled multi image. // If you make it more than 1, make sure to enable the below script for the post form to change. - // $config['additional_javascript'][] = 'js/multi_image.js'; + // $config['additional_javascript'][] = 'js/multi-image.js'; $config['max_images'] = 1; // Method to use for determing the max filesize. From 6049979dafcdbf17410a15ab250577e222c11bbe Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 9 Oct 2016 02:01:23 +0200 Subject: [PATCH 166/212] nntpchan install.sql fix --- install.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install.sql b/install.sql index 3b390645..23919d2f 100644 --- a/install.sql +++ b/install.sql @@ -321,7 +321,7 @@ CREATE TABLE `pages` ( -- CREATE TABLE `nntp_references` ( - `board` varchar(60) NOT NULL, + `board` varchar(30) NOT NULL, `id` int(11) unsigned NOT NULL, `message_id` varchar(255) CHARACTER SET ascii NOT NULL, `message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL, @@ -336,3 +336,4 @@ CREATE TABLE `nntp_references` ( /*!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 */; + From c21eeff6057a50230b4ba3574ba19757995b3546 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 9 Oct 2016 02:02:13 +0200 Subject: [PATCH 167/212] local-time.js: relative time by default --- js/local-time.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/local-time.js b/js/local-time.js index a821579f..c3c47b8d 100644 --- a/js/local-time.js +++ b/js/local-time.js @@ -89,7 +89,7 @@ $(document).ready(function(){ Options.extend_tab('general', ''); $('#show-relative-time>input').on('change', function() { - if (localStorage.show_relative_time === 'true') { + if (localStorage.show_relative_time !== 'false') { localStorage.show_relative_time = 'false'; clearInterval(interval_id); } else { @@ -100,7 +100,7 @@ $(document).ready(function(){ do_localtime(document); }); - if (localStorage.show_relative_time === 'true') { + if (localStorage.show_relative_time !== 'false') { $('#show-relative-time>input').attr('checked','checked'); interval_id = setInterval(do_localtime, 30000, document); } From 65f39b8a07adb70ac8ac7c82ba2d2dab927459c6 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 9 Oct 2016 02:11:55 +0200 Subject: [PATCH 168/212] local-time.js: fixup --- js/local-time.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/local-time.js b/js/local-time.js index c3c47b8d..1a05002b 100644 --- a/js/local-time.js +++ b/js/local-time.js @@ -73,7 +73,7 @@ $(document).ready(function(){ times[i].setAttribute('data-local', 'true'); - if (!localStorage.show_relative_time || localStorage.show_relative_time === 'false') { + if (localStorage.show_relative_time === 'false') { times[i].innerHTML = dateformat(iso8601(t)); times[i].setAttribute('title', timeDifference(currentTime, postTime.getTime())); } else { From 2a669d71131213a0abd95c8fec1ec0b0f18cd524 Mon Sep 17 00:00:00 2001 From: pngcrypt Date: Sun, 20 Nov 2016 03:13:17 +0300 Subject: [PATCH 169/212] table `pages` fix --- install.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sql b/install.sql index 23919d2f..1acc849f 100644 --- a/install.sql +++ b/install.sql @@ -305,8 +305,8 @@ CREATE TABLE IF NOT EXISTS `ban_appeals` ( CREATE TABLE `pages` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `board` varchar(255) DEFAULT NULL, - `name` varchar(255) NOT NULL, + `board` varchar(58) CHARACTER SET utf8 DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8 NOT NULL, `title` varchar(255) DEFAULT NULL, `type` varchar(255) DEFAULT NULL, `content` text, From 1f4de533f039b668cfadcf0c1aba27e990987373 Mon Sep 17 00:00:00 2001 From: Montrosos Date: Mon, 12 Dec 2016 13:52:42 +0100 Subject: [PATCH 170/212] Included header.html for better boardlist Simply included the header.html so that the compact boardlist works with it and it's responsive now. --- templates/themes/recent/recent.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/themes/recent/recent.html b/templates/themes/recent/recent.html index 208c1088..27e64590 100644 --- a/templates/themes/recent/recent.html +++ b/templates/themes/recent/recent.html @@ -9,6 +9,7 @@ {% if config.url_favicon %}{% endif %} {% if config.default_stylesheet.1 != '' %}{% endif %} {% if config.font_awesome %}{% endif %} + {% include 'header.html' %} {{ boardlist.top }} From b3071152dc82638a35e55267258b385337aa41c7 Mon Sep 17 00:00:00 2001 From: int15h Date: Sat, 24 Dec 2016 10:39:30 -0200 Subject: [PATCH 171/212] new exif provider --- templates/post/image_identification.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/post/image_identification.html b/templates/post/image_identification.html index e6e14abc..3d06bd00 100644 --- a/templates/post/image_identification.html +++ b/templates/post/image_identification.html @@ -5,7 +5,7 @@ ImgOps {% endif %} {% if config.image_identification_exif and file.file|extension == 'jpg' %} - Exif + Exif {% endif %} {% if config.image_identification_google %} Google From 50d2cfd45e6c248aea431b044b9168918900bf16 Mon Sep 17 00:00:00 2001 From: tlm-2501 <13thSlayer@gmail.com> Date: Wed, 18 Jan 2017 06:21:56 +0300 Subject: [PATCH 172/212] Change "Configuration Basics" link to point to the archived tinyboard website. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e153ab7..8881e6e5 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Installation Please remember to change the administrator account password. -See also: [Configuration Basics](http://tinyboard.org/docs/?p=Config). +See also: [Configuration Basics](https://web.archive.org/web/20121003095922/http://tinyboard.org/docs/?p=Config). Upgrade ------- From 2ca02733acb12083f733c23504c159feb89c95f0 Mon Sep 17 00:00:00 2001 From: SHooZ Date: Sun, 22 Jan 2017 22:16:56 +0000 Subject: [PATCH 173/212] Add uk_UA localization files --- inc/locale/uk_UA/LC_MESSAGES/javascript.po | 867 +++++ inc/locale/uk_UA/LC_MESSAGES/tinyboard.mo | Bin 0 -> 30990 bytes inc/locale/uk_UA/LC_MESSAGES/tinyboard.po | 3542 ++++++++++++++++++++ 3 files changed, 4409 insertions(+) create mode 100644 inc/locale/uk_UA/LC_MESSAGES/javascript.po create mode 100644 inc/locale/uk_UA/LC_MESSAGES/tinyboard.mo create mode 100644 inc/locale/uk_UA/LC_MESSAGES/tinyboard.po diff --git a/inc/locale/uk_UA/LC_MESSAGES/javascript.po b/inc/locale/uk_UA/LC_MESSAGES/javascript.po new file mode 100644 index 00000000..68d96ce8 --- /dev/null +++ b/inc/locale/uk_UA/LC_MESSAGES/javascript.po @@ -0,0 +1,867 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-10-18 13:47+0200\n" +"PO-Revision-Date: 2016-06-19 17:30+0300\n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Last-Translator: kotobenko \n" +"Language-Team: \n" +"X-Generator: Poedit 1.8.8\n" + +#: ../../../../js/style-select.js:40 ../../../../js/style-select.js:41 +msgid "Style: " +msgstr "Стиль:" + +#: ../../../../js/hide-images.js:50 ../../../../js/upload-selection.js:51 +#: ../../../../js/quick-post-controls.js:30 ../../../../js/hide-images.js:51 +#: ../../../../js/quick-post-controls.js:32 +#: ../../../../js/upload-selection.js:61 +#: ../../../../js/upload-selection.js:69 +msgid "File" +msgstr "Файл" + +#: ../../../../js/hide-images.js:50 ../../../../js/hide-images.js:51 +msgid "hide" +msgstr "приховати" + +#: ../../../../js/hide-images.js:56 ../../../../js/hide-images.js:57 +#: ../../../../js/hide-images.js:63 +msgid "show" +msgstr "показати" + +#: ../../../../js/toggle-locked-threads.js:39 +#: ../../../../js/toggle-locked-threads.js:54 +#: ../../../../js/toggle-locked-threads.js:40 +#: ../../../../js/toggle-locked-threads.js:55 +#: ../../../../js/toggle-locked-threads.js:41 +#: ../../../../js/toggle-locked-threads.js:56 +#: ../../../../js/toggle-locked-threads.js:53 +#: ../../../../js/toggle-locked-threads.js:70 +msgid "Show locked threads" +msgstr "Показати закріплені нитки" + +#: ../../../../js/toggle-locked-threads.js:39 +#: ../../../../js/toggle-locked-threads.js:54 +#: ../../../../js/toggle-locked-threads.js:40 +#: ../../../../js/toggle-locked-threads.js:55 +#: ../../../../js/toggle-locked-threads.js:41 +#: ../../../../js/toggle-locked-threads.js:56 +#: ../../../../js/toggle-locked-threads.js:44 +#: ../../../../js/toggle-locked-threads.js:53 +#: ../../../../js/toggle-locked-threads.js:70 +msgid "Hide locked threads" +msgstr "Приховати закріплені нитки" + +#: ../../../../js/upload-selection.js:32 +#: ../../../../js/upload-selection.js:45 +#: ../../../../js/upload-selection.js:53 +msgid "URL" +msgstr "URL" + +#: ../../../../js/upload-selection.js:50 +#: ../../../../js/upload-selection.js:60 +#: ../../../../js/upload-selection.js:68 +msgid "Select" +msgstr "Вибрати" + +#: ../../../../js/upload-selection.js:53 +#: ../../../../js/upload-selection.js:63 +#: ../../../../js/upload-selection.js:71 +msgid "Remote" +msgstr "" + +#: ../../../../js/upload-selection.js:56 +#: ../../../../js/upload-selection.js:66 +#: ../../../../js/upload-selection.js:74 +msgid "Embed" +msgstr "Вбудувати" + +#: ../../../../js/upload-selection.js:59 +#: ../../../../js/upload-selection.js:69 +#: ../../../../js/upload-selection.js:77 +msgid "Oekaki" +msgstr "Оекакі" + +#: ../../../../js/toggle-images.js:41 ../../../../js/toggle-images.js:42 +#: ../../../../js/toggle-images.js:45 +msgid "hidden" +msgstr "" + +#: ../../../../js/toggle-images.js:57 ../../../../js/toggle-images.js:70 +#: ../../../../js/toggle-images.js:58 ../../../../js/toggle-images.js:71 +#: ../../../../js/toggle-images.js:86 +msgid "Show images" +msgstr "Показати зображення" + +#: ../../../../js/toggle-images.js:57 ../../../../js/toggle-images.js:70 +#: ../../../../js/toggle-images.js:58 ../../../../js/toggle-images.js:71 +#: ../../../../js/toggle-images.js:63 ../../../../js/toggle-images.js:86 +msgid "Hide images" +msgstr "Приховати зображення" + +#: ../../../../js/quick-post-controls.js:27 +#: ../../../../js/quick-post-controls.js:29 +msgid "Password" +msgstr "Пароль" + +#: ../../../../js/quick-post-controls.js:29 +#: ../../../../js/quick-post-controls.js:31 +msgid "Delete file only" +msgstr "Видалити тільки файл" + +#: ../../../../js/quick-post-controls.js:31 +#: ../../../../js/quick-post-controls.js:33 +msgid "Delete" +msgstr "Видалити" + +#: ../../../../js/quick-post-controls.js:35 +#: ../../../../js/quick-post-controls.js:37 +#: ../../../../js/mod/ban-list.js:40 +msgid "Reason" +msgstr "Причина" + +#: ../../../../js/quick-post-controls.js:37 +#: ../../../../js/quick-post-controls.js:39 +msgid "Report" +msgstr "Повідомити адміністрацію" + +#: ../../../../js/expand.js:20 ../../../../js/expand.js:22 +msgid "Click reply to view." +msgstr "Натисніть «Відповісти», щоб переглянути нитку повністю" + +#: ../../../../js/expand.js:20 ../../../../js/expand.js:22 +#: ../../../../js/live-index.js:72 ../../../../js/live-index.js:83 +msgid "Click to expand" +msgstr "Показати повністю" + +#: ../../../../js/expand.js:44 ../../../../js/expand.js:46 +#: ../../../../js/expand.js:50 +msgid "Hide expanded replies" +msgstr "Приховати розгорнуті дописи" + +#: ../../../../js/oekaki.js:10 +msgid "Brush size" +msgstr "Розмір пензля" + +#: ../../../../js/oekaki.js:10 +msgid "Set text" +msgstr "Додати текст" + +#: ../../../../js/oekaki.js:10 +msgid "Clear" +msgstr "Очистити" + +#: ../../../../js/oekaki.js:10 +msgid "Save" +msgstr "Зберегти" + +#: ../../../../js/oekaki.js:10 +msgid "Load" +msgstr "Завантажити" + +#: ../../../../js/oekaki.js:11 +msgid "Toggle eraser" +msgstr "Увімкнути стирачку" + +#: ../../../../js/oekaki.js:11 +msgid "Get color" +msgstr "Отримати колір" + +#: ../../../../js/oekaki.js:11 +msgid "Fill" +msgstr "Залити" + +#: ../../../../js/oekaki.js:12 +msgid "Use oekaki instead of file?" +msgstr "Використати оекакі замість файла?" + +#: ../../../../js/oekaki.js:21 +msgid "Edit in oekaki" +msgstr "Відкрити в редакторі оекакі" + +#: ../../../../js/oekaki.js:152 +msgid "Enter some text" +msgstr "Введіть текст" + +#: ../../../../js/oekaki.js:153 +msgid "Enter font or leave empty" +msgstr "Введіть назву шрифту або залиште порожнім" + +#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:65 +#: ../../../../js/forced-anon.js:69 ../../../../js/forced-anon.js:60 +#: ../../../../js/forced-anon.js:66 ../../../../js/forced-anon.js:70 +#: ../../../../js/forced-anon.js:61 ../../../../js/forced-anon.js:67 +#: ../../../../js/forced-anon.js:71 ../../../../js/forced-anon.js:73 +#: ../../../../js/forced-anon.js:81 ../../../../js/forced-anon.js:85 +msgid "Forced anonymity" +msgstr "Примусова анонімність" + +#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:65 +#: ../../../../js/forced-anon.js:60 ../../../../js/forced-anon.js:66 +#: ../../../../js/forced-anon.js:61 ../../../../js/forced-anon.js:67 +#: ../../../../js/forced-anon.js:73 ../../../../js/forced-anon.js:81 +msgid "enabled" +msgstr "увімкнено" + +#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:69 +#: ../../../../js/forced-anon.js:60 ../../../../js/forced-anon.js:70 +#: ../../../../js/forced-anon.js:61 ../../../../js/forced-anon.js:71 +#: ../../../../js/forced-anon.js:73 ../../../../js/forced-anon.js:85 +msgid "disabled" +msgstr "вимкнено" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Sun" +msgstr "НД" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Mon" +msgstr "ПН" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Tue" +msgstr "ВТ" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Wed" +msgstr "СР" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Thu" +msgstr "ЧТ" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Fri" +msgstr "ПТ" + +#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41 +#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63 +msgid "Sat" +msgstr "СБ" + +#: ../../../../js/catalog-link.js:21 ../../../../js/catalog-link.js:32 +#: ../../../../js/catalog-link.js:40 ../../../../js/catalog-link.js:33 +#: ../../../../js/catalog-link.js:44 ../../../../js/catalog-link.js:52 +#: ../../../../js/catalog-link.js:28 ../../../../js/catalog-link.js:39 +#: ../../../../js/catalog-link.js:47 +msgid "Catalog" +msgstr "Каталог" + +#: ../../../../js/quick-reply.js:21 ../../../../js/quick-reply-old.js:21 +#: ../../../../js/quick-reply-old.js:23 +msgid "Submit" +msgstr "Розмістити" + +#: ../../../../js/quick-reply.js:31 ../../../../js/quick-reply-old.js:31 +#: ../../../../js/quick-reply-old.js:33 +msgid "Quick reply" +msgstr "Відповісти швидко" + +#: ../../../../js/quick-reply.js:33 ../../../../js/quick-reply-old.js:33 +#: ../../../../js/quick-reply-old.js:35 +#, python-brace-format +msgid "Posting mode: Replying to >>{0}" +msgstr "Режим розміщення: Відповідь >>{0}" + +#: ../../../../js/quick-reply.js:33 ../../../../js/quick-reply-old.js:33 +#: ../../../../js/quick-reply-old.js:35 +msgid "Return" +msgstr "Повернутися" + +#: ../../../../js/expand-all-images.js:20 +#: ../../../../js/expand-all-images.js:21 +#: ../../../../js/expand-all-images.js:22 +#: ../../../../js/expand-all-images.js:23 +msgid "Expand all images" +msgstr "Розгорнути всі зображення" + +#: ../../../../templates/main.js:6 +msgid "Hello!" +msgstr "Привіт!" + +#: ../../../../templates/main.js:18 +#, python-brace-format +msgid "{0} users" +msgstr "{0} користувачів" + +#: ../../../../templates/themes/ukko/ukko.js:28 +#: ../../../../templates/themes/ukko/ukko.js:39 +#: ../../../../templates/themes/ukko/ukko.js:29 +#: ../../../../templates/themes/ukko/ukko.js:40 +#: ../../../../templates/themes/ukko/ukko.js:52 +#: ../../../../templates/themes/ukko/ukko.js:63 +msgid "(hide threads from this board)" +msgstr "(приховати нитки цієї дошки)" + +#: ../../../../templates/themes/ukko/ukko.js:32 +#: ../../../../templates/themes/ukko/ukko.js:44 +#: ../../../../templates/themes/ukko/ukko.js:33 +#: ../../../../templates/themes/ukko/ukko.js:45 +#: ../../../../templates/themes/ukko/ukko.js:56 +#: ../../../../templates/themes/ukko/ukko.js:68 +msgid "(show threads from this board)" +msgstr "(показати нитки цієї дошки)" + +#: ../../../../templates/themes/ukko/ukko.js:57 +#: ../../../../templates/themes/ukko/ukko.js:58 +#: ../../../../templates/themes/ukko/ukko.js:81 +msgid "No more threads to display" +msgstr "Було показано всі нитки" + +#: ../../../../templates/themes/ukko/ukko.js:79 +#: ../../../../templates/themes/ukko/ukko.js:80 +#: ../../../../templates/themes/ukko/ukko.js:103 +#: ../../../../js/infinite-scroll.js:48 +msgid "Loading..." +msgstr "Завантаження…" + +#: ../../../../js/download-original.js:32 +#: ../../../../js/download-original.js:33 +msgid "Save as original filename" +msgstr "Зберегти з початковим іменем файла" + +#: ../../../../js/ajax-post-controls.js:43 +msgid "Reported post(s)." +msgstr "Допис(и), про які повідомлено." + +#: ../../../../js/ajax-post-controls.js:53 +msgid "An unknown error occured!" +msgstr "Сталася невідома помилка!" + +#: ../../../../js/ajax-post-controls.js:60 +msgid "Something went wrong... An unknown error occured!" +msgstr "Щось пішло не так… Сталася невідома помилка!" + +#: ../../../../js/ajax-post-controls.js:68 +msgid "Working..." +msgstr "" + +#: ../../../../js/ajax.js:42 ../../../../js/ajax.js:45 +msgid "Posting... (#%)" +msgstr "Розміщення… (#%)" + +#: ../../../../js/ajax.js:104 ../../../../js/ajax.js:109 +msgid "Posted..." +msgstr "Розміщено…" + +#: ../../../../js/ajax.js:106 ../../../../js/ajax.js:111 +msgid "An unknown error occured when posting!" +msgstr "Під час розміщення сталася невідома помилка!" + +#: ../../../../js/ajax.js:130 ../../../../js/ajax.js:135 +msgid "Posting..." +msgstr "Розміщення…" + +#: ../../../../js/quick-reply.js:223 ../../../../js/quick-reply.js:224 +#: ../../../../js/quick-reply.js:225 +msgid "Upload URL" +msgstr "Адреса завантаження" + +#: ../../../../js/quick-reply.js:266 ../../../../js/quick-reply.js:267 +#: ../../../../js/quick-reply.js:268 +msgid "Spoiler Image" +msgstr "Спойлерне зображення" + +#: ../../../../js/quick-reply.js:277 ../../../../js/quick-reply.js:278 +#: ../../../../js/quick-reply.js:279 ../../../../js/quick-reply.js:281 +msgid "Comment" +msgstr "Коментувати" + +#: ../../../../js/quick-reply.js:285 ../../../../js/quick-reply.js:406 +#: ../../../../js/quick-reply.js:286 ../../../../js/quick-reply.js:407 +#: ../../../../js/quick-reply.js:287 ../../../../js/quick-reply.js:408 +#: ../../../../js/quick-reply.js:289 ../../../../js/quick-reply.js:410 +msgid "Quick Reply" +msgstr "Швидка відповідь" + +#: ../../../../js/watch.js:249 ../../../../js/watch.js:250 +#: ../../../../js/watch.js:288 ../../../../js/watch.js:289 +#: ../../../../js/watch.js:330 ../../../../js/watch.js:331 +#: ../../../../js/watch.js:361 ../../../../js/watch.js:362 +msgid "Stop watching this thread" +msgstr "Припинити стеження за ниткою" + +#: ../../../../js/watch.js:249 ../../../../js/watch.js:250 +#: ../../../../js/watch.js:288 ../../../../js/watch.js:289 +#: ../../../../js/watch.js:330 ../../../../js/watch.js:331 +#: ../../../../js/watch.js:361 ../../../../js/watch.js:362 +msgid "Watch this thread" +msgstr "Стежити за ниткою" + +#: ../../../../js/watch.js:260 ../../../../js/watch.js:261 +#: ../../../../js/watch.js:269 ../../../../js/watch.js:299 +#: ../../../../js/watch.js:300 ../../../../js/watch.js:308 +#: ../../../../js/watch.js:341 ../../../../js/watch.js:342 +#: ../../../../js/watch.js:350 ../../../../js/watch.js:372 +#: ../../../../js/watch.js:373 ../../../../js/watch.js:381 +msgid "Unpin this board" +msgstr "Відкріпити дошку" + +#: ../../../../js/watch.js:260 ../../../../js/watch.js:261 +#: ../../../../js/watch.js:269 ../../../../js/watch.js:299 +#: ../../../../js/watch.js:300 ../../../../js/watch.js:308 +#: ../../../../js/watch.js:341 ../../../../js/watch.js:342 +#: ../../../../js/watch.js:350 ../../../../js/watch.js:372 +#: ../../../../js/watch.js:373 ../../../../js/watch.js:381 +msgid "Pin this board" +msgstr "Прикріпити дошку" + +#: ../../../../js/watch.js:262 ../../../../js/watch.js:267 +#: ../../../../js/watch.js:268 ../../../../js/watch.js:301 +#: ../../../../js/watch.js:306 ../../../../js/watch.js:307 +#: ../../../../js/watch.js:343 ../../../../js/watch.js:348 +#: ../../../../js/watch.js:349 ../../../../js/watch.js:374 +#: ../../../../js/watch.js:379 ../../../../js/watch.js:380 +msgid "Stop watching this board" +msgstr "Не стежити за дошкою" + +#: ../../../../js/watch.js:262 ../../../../js/watch.js:267 +#: ../../../../js/watch.js:268 ../../../../js/watch.js:301 +#: ../../../../js/watch.js:306 ../../../../js/watch.js:307 +#: ../../../../js/watch.js:343 ../../../../js/watch.js:348 +#: ../../../../js/watch.js:349 ../../../../js/watch.js:374 +#: ../../../../js/watch.js:379 ../../../../js/watch.js:380 +msgid "Watch this board" +msgstr "Стежити за дошкою" + +#: ../../../../js/wpaint.js:113 +msgid "Click on any image on this site to load it into oekaki applet" +msgstr "" +"Клацніть по будь-якому зображенні на сторінці, щоб завантажити його до " +"аплету оекакі" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Sunday" +msgstr "Неділя" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Monday" +msgstr "Понеділок" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Tuesday" +msgstr "Вівторок" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Wednesday" +msgstr "Середа" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Thursday" +msgstr "Четвер" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Friday" +msgstr "Пʼятниця" + +#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62 +msgid "Saturday" +msgstr "Субота" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "January" +msgstr "Січень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "February" +msgstr "Лютий" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "March" +msgstr "Березень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "April" +msgstr "Квітень" + +#: ../../../../js/local-time.js:31 ../../../../js/local-time.js:32 +#: ../../../../templates/main.js:64 ../../../../templates/main.js:65 +msgid "May" +msgstr "Травень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "June" +msgstr "Червень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "July" +msgstr "Липень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "August" +msgstr "Серпень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "September" +msgstr "Вересень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "October" +msgstr "Жовтень" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "November" +msgstr "Листопад" + +#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64 +msgid "December" +msgstr "Грудень" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Jan" +msgstr "Січ" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Feb" +msgstr "Лют" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Mar" +msgstr "Бер" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Apr" +msgstr "Кві" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Jun" +msgstr "Чер" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Jul" +msgstr "Лип" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Aug" +msgstr "Сер" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Sep" +msgstr "Вер" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Oct" +msgstr "Жов" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Nov" +msgstr "Лис" + +#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65 +msgid "Dec" +msgstr "Гру" + +#: ../../../../js/local-time.js:33 ../../../../templates/main.js:66 +msgid "AM" +msgstr "AM" + +#: ../../../../js/local-time.js:34 ../../../../templates/main.js:67 +msgid "PM" +msgstr "PM" + +#: ../../../../js/local-time.js:35 ../../../../templates/main.js:68 +msgid "am" +msgstr "am" + +#: ../../../../js/local-time.js:36 ../../../../templates/main.js:69 +msgid "pm" +msgstr "pm" + +#: ../../../../js/expand-video.js:45 ../../../../js/expand-video.js:48 +msgid "Your browser does not support HTML5 video." +msgstr "Ваш браузер не підтримує відео HTML5" + +#: ../../../../js/expand-video.js:189 ../../../../js/expand-video.js:192 +#: ../../../../js/expand-video.js:193 +msgid "[play once]" +msgstr "[відтворити раз]" + +#: ../../../../js/expand-video.js:190 ../../../../js/expand-video.js:193 +#: ../../../../js/expand-video.js:194 +msgid "[loop]" +msgstr "[зациклити]" + +#: ../../../../js/webm-settings.js:42 ../../../../js/webm-settings.js:45 +msgid "WebM Settings" +msgstr "Налаштування WebM" + +#: ../../../../js/webm-settings.js:44 ../../../../js/webm-settings.js:54 +msgid "Expand videos inline" +msgstr "Розгорнути відео на місці" + +#: ../../../../js/webm-settings.js:45 ../../../../js/webm-settings.js:55 +msgid "Play videos on hover" +msgstr "Відтворити відео в пливучому віконці" + +#: ../../../../js/webm-settings.js:46 ../../../../js/webm-settings.js:56 +msgid "Default volume" +msgstr "Гучність за промовчанням" + +#: ../../../../js/treeview.js:18 +msgid "Tree view" +msgstr "Перегляд деревом" + +#: ../../../../js/expand-all-images.js:32 +#: ../../../../js/expand-all-images.js:35 +msgid "Shrink all images" +msgstr "Позгортати всі зображення" + +#: ../../../../js/no-animated-gif.js:33 ../../../../js/no-animated-gif.js:37 +#: ../../../../js/no-animated-gif.js:40 +msgid "Animate GIFs" +msgstr "Увімкнути анімацію GIF" + +#: ../../../../js/no-animated-gif.js:42 ../../../../js/no-animated-gif.js:48 +#: ../../../../js/no-animated-gif.js:47 ../../../../js/no-animated-gif.js:57 +#: ../../../../js/no-animated-gif.js:62 ../../../../js/no-animated-gif.js:50 +#: ../../../../js/no-animated-gif.js:60 ../../../../js/no-animated-gif.js:65 +msgid "Unanimate GIFs" +msgstr "Вимкнути анімацію GIF" + +#: ../../../../js/webm-settings.js:41 +msgid "WebM" +msgstr "WebM" + +#: ../../../../js/live-index.js:25 ../../../../js/live-index.js:84 +msgid "No new posts." +msgstr "Немає нових дописів." + +#: ../../../../js/live-index.js:30 ../../../../js/live-index.js:73 +msgid "No new threads." +msgstr "Немає нових ниток." + +#: ../../../../js/live-index.js:72 +#, python-brace-format +msgid "There are {0} new threads." +msgstr "{0} нових ниток." + +#: ../../../../js/live-index.js:83 +#, python-brace-format +msgid "There are {0} new posts in this thread." +msgstr "{0} нових дописів у цій нитці." + +#: ../../../../js/options.js:106 +msgid "Options" +msgstr "Налаштування" + +#: ../../../../js/options/general.js:15 +msgid "General" +msgstr "Основні" + +#: ../../../../js/options/general.js:18 +msgid "Storage: " +msgstr "Сховище:" + +#: ../../../../js/options/general.js:21 +msgid "Export" +msgstr "Експортувати" + +#: ../../../../js/options/general.js:27 +msgid "Import" +msgstr "Імпортувати" + +#: ../../../../js/options/general.js:28 +msgid "Paste your storage data" +msgstr "Вставити дані зі сховища" + +#: ../../../../js/options/general.js:40 +msgid "Erase" +msgstr "Очистити" + +#: ../../../../js/options/general.js:41 +msgid "" +"Are you sure you want to erase your storage? This involves your hidden " +"threads, watched threads, post password and many more." +msgstr "" +"Ви впевнені, що хочете очистити своє сховище? Списки прихованих і " +"переглянутих ниток, пароль дописування та багато іншого буде анульовано." + +#: ../../../../js/options/user-css.js:14 +msgid "User CSS" +msgstr "Користувацький CSS" + +#: ../../../../js/options/user-css.js:23 +msgid "Update custom CSS" +msgstr "Оновити користувацький CSS" + +#: ../../../../js/options/user-css.js:45 +msgid "Enter here your own CSS rules..." +msgstr "Введіть власні правила CSS…" + +#: ../../../../js/options/user-css.js:46 +msgid "" +"If you want to make a redistributable style, be sure to\n" +" have a Yotsuba B theme selected." +msgstr "" +"Якщо хочете, щоб ваш стиль був переносним,\n" +"впевніться, що обрано тему Yotsuba B." + +#: ../../../../js/options/user-css.js:47 +msgid "You can include CSS files from remote servers, for example:" +msgstr "Ви можете включати файли CSS із віддалених серверів, наприклад:" + +#: ../../../../js/options/user-js.js:14 +msgid "User JS" +msgstr "Користувацький JS" + +#: ../../../../js/options/user-js.js:23 +msgid "Update custom Javascript" +msgstr "Оновити користувацький JS" + +#: ../../../../js/options/user-js.js:54 +msgid "Enter here your own Javascript code..." +msgstr "Введіть власний код Javascript…" + +#: ../../../../js/options/user-js.js:55 +msgid "" +"Have a backup of your storage somewhere, as messing here\n" +" may render you this website unusable." +msgstr "" +"Створіть десь в іншому місці резервну копію сховища,\n" +"бо може статись, що після змін тут ви будете не в змозі користуватися цим " +"сайтом." + +#: ../../../../js/options/user-js.js:56 +msgid "You can include JS files from remote servers, for example:" +msgstr "Ви можете включати файли CSS із віддалених серверів, наприклад:" + +#: ../../../../js/id_colors.js:6 ../../../../js/id_colors.js:12 +msgid "Color IDs" +msgstr "" + +#: ../../../../js/auto-reload.js:33 +msgid "Update" +msgstr "Оновлення" + +#: ../../../../js/mod/ban-list.js:29 +msgid "IP address" +msgstr "IP адреса" + +#: ../../../../js/mod/ban-list.js:42 +msgid "Seen" +msgstr "" + +#: ../../../../js/mod/ban-list.js:44 +msgid "Message for which user was banned is included" +msgstr "Повідомлення, за яке користувача заблоковано, додається" + +#: ../../../../js/mod/ban-list.js:45 +msgid "Message:" +msgstr "Повідомлення:" + +#: ../../../../js/mod/ban-list.js:53 +msgid "Board" +msgstr "Дошка" + +#: ../../../../js/mod/ban-list.js:55 +msgid "all" +msgstr "все" + +#: ../../../../js/mod/ban-list.js:57 +msgid "Set" +msgstr "Встановити" + +#: ../../../../js/mod/ban-list.js:58 +msgid " ago" +msgstr " тому" + +#: ../../../../js/mod/ban-list.js:61 +msgid "Expires" +msgstr "" +"Сплива\n" +"є" + +#: ../../../../js/mod/ban-list.js:62 +msgid "never" +msgstr "ніколи" + +#: ../../../../js/mod/ban-list.js:64 +msgid "in " +msgstr "в" + +#: ../../../../js/mod/ban-list.js:66 +msgid "Staff" +msgstr "Модератор" + +#: ../../../../js/mod/ban-list.js:73 +msgid "system" +msgstr "система" + +#: ../../../../js/auto-reload.js:33 +msgid "Auto" +msgstr "Авто" + +#: ../../../../js/auto-reload.js:141 +msgid "Updating..." +msgstr "Оновлення…" + +#: ../../../../js/auto-reload.js:183 +#, python-brace-format +msgid "Thread updated with {0} new post(s)" +msgstr "Нитку оновлено, зʼявилось {0} допис(ів)" + +#: ../../../../js/auto-reload.js:185 +msgid "No new posts found" +msgstr "Нових дописів немає" + +# Що мається на увазі під «pruned»? +#: ../../../../js/auto-reload.js:191 +msgid "Thread deleted or pruned" +msgstr "Нитку видалено або вилучено" + +#: ../../../../js/auto-reload.js:199 +msgid "Error: " +msgstr "Помилка:" + +#: ../../../../js/auto-reload.js:201 +msgid "Unknown error" +msgstr "Невідома помилка" + +#: ../../../../js/infinite-scroll.js:47 +msgid "Page" +msgstr "Сторінка" + +#: ../../../../js/infinite-scroll.js:82 +msgid "All" +msgstr "Всі" + +#: ../../../../templates/main.js:29 ../../../../templates/main.js:47 +msgid "second(s)" +msgstr "секунд(и)" + +#: ../../../../templates/main.js:31 ../../../../templates/main.js:49 +msgid "minute(s)" +msgstr "хвилин(и)" + +#: ../../../../templates/main.js:33 ../../../../templates/main.js:51 +msgid "hour(s)" +msgstr "годин(и)" + +#: ../../../../templates/main.js:35 ../../../../templates/main.js:53 +msgid "day(s)" +msgstr "день(дні)" + +#: ../../../../templates/main.js:37 ../../../../templates/main.js:55 +msgid "week(s)" +msgstr "тиждень(тижнів)" + +#: ../../../../templates/main.js:39 ../../../../templates/main.js:57 +msgid "year(s)" +msgstr "рік(років)" diff --git a/inc/locale/uk_UA/LC_MESSAGES/tinyboard.mo b/inc/locale/uk_UA/LC_MESSAGES/tinyboard.mo new file mode 100644 index 0000000000000000000000000000000000000000..7454538d775bf265be4aec7e5b9a61b977ee8786 GIT binary patch literal 30990 zcmeI4dzf5ho#&6B3`Rgv1W}aJfdGMYr<;pl=Vn3@2qw^^6A_sKtGiAoWp`DzbxEhy zVMAgBSBAtG5(gz*;$_rzY;z$Axvh-r&N_@%4UXeJ&gkmSPVKM_%2)*YbFj@;Tr) z!B>Jm0^g%9=LW$UzeSHXu{u$s3up8_K?*?BEeja=k zcpTKc&w$#`w>&-zijIE;#g8*#y6Ct7lw4+jSAvVcH-J@e0k|C$zrPN?0{jK2eZ2H_ zZvIz;{0lDPkNCI%RKI1Q_Vad7`?(7g{gH1U1x4>YzWzZ_`+XGD{GS35S+Ezp4E&0( z|0yW?f9uO z`O~2E?5{xa<$Q=m^g%bMdENq=TtT`75h(t@4-|b{LCNK#p!o2(Z~qjiaeICHAyDK0 zBRC)Y4yg6s5IVVC14@r?1Z7We1s8*Zp!W4=pyWCMYF~#yt^b!GDiS;cimo4nh${Fs zsCiz)CPmi_Q1e_4YTT8e_&?9%4Zi(WU%nmGdV?NEJXS%?celq)pyYNRsP#YS>vwwm zn6G~f6rVl^iZA;>je8Q*I!8gt_shQg4N&xc$KyYNh&cE$sD5W6bjkJgpyYH7xDZ_C z%O3=_&Ldz5egf1wUj{Y)YoN^!O27UI6n(z{)$g~yd^%1+{ay~r9?k>B*O{R9`$kaw zTm)*p6~4Y7)Vz0rns?OKkAWJ$$(OhK_K$(u$8PXc@BpZOhe3`1OYoT&K{qJ*@1Ei0 zum@DX10KHwo=Uk6$`Af6$iLv9`STL+>`UB!&I2{>LQr})%a?mW^}7K)4P4=I6(~O3 z0cyTMP~$%UYWxmxCirpiP2g9-%fZtx4T2umlp1EwR}sJL<~sQMxBX7JtMX7Im& zurRm<<{`>p9F)8s1GV430HsIY^7tZzq4K35LxUBd_WK@?Ee8*R?*P9Dir(qnZv8id z3n{M#=YTswje7>vxF3O9|9q5C^ehBL_prwe;MJ5L1QkEO0*2r}fY*V~flLuxOVY3s zTnmc6!=U*2EGRzx416W{3sCXpWmh@>w$S5ZP;$8el-&A!`8H5`^bYV9U<68D>pI?ASga;17*Lv!Lz}qK>h_^_w{G>x_+JDMbys+aoNEDSOqtO ze+d2>ya{|8%E4o>8(ai_8=MDr%ys?l0_As(fR}^MfNut$1EoI;=qx*518Sf5dVB;t zhw=eX{Ce8s-+BBVC_elMd@uMaHopek3W_hk05PrLoCR+DLQs0T9J~Nr=gS`kUr+fc zsP(=D-U|K%ybE0P<{(%CJ`Sz}e*oSL-i2{o4E{N&eLMqdo?nBgc5pt-xDb2`C_WB@ z($g)V`0)`?bbcO`p8XZ5aX<6*mt61Uun1JYeh?D~ir@hUumTL}(@O~Q9JgY&`yB3rljf1Cw`#|yOFnBTeH=y+3U%^wr zQ*Lzl5>R$@IyeJ-4ftAcJ}AE24&Da72bA2N0VU_@%bZ-U1*cQK1C)Qc8;xBso52$J8t@k$&%VX=yBwTJ{Y{|spaP1X zL!kKmEl~5G+vntSHK_7D5LXl20ZRWq44w^s-nV}dd==&IfZE4TLGAapYTx(z_HCfn-R|e;4>N@NV$s z;J*i@w-15Z=QH5t;Lkx^c@XxK1AwbQ$@`n2<~eJX+wbeZS5oc=weFBFZ}sKfp!W0E zpw{~~coz6SLGl0JKiGCNk3FFH zaszlS7=d%Z_kyQ_2SLf_2q=Dj6MO>={~gr+PkFne_hL|Tx(2)gyaf~;?*Zq5Tfq$Y zRZ#ouzQg(Bg`oQ14vOB*;B4@7p!oC;p!oL_(Dn~XZs-0Xdj>BAMaO#Zb>Ktb0QdyB z5Ip~noc-JZ;{1CVt{1SKpc=lbyVz3Jw z1FK*c_#;qsU+@mMpXDCcgVK+WgXe-@10|>LgCTeZOp*WS1TpDgGk7Lg2j2yL2h4-F zvZ>p_BK<)Dzpy*$`*2S&Iz|$yy1r&XCP;xsphfRW)fLiAh;6>nn0uO-Cg3p1E zy_1**J~|99KHqAx?`a9d@rbePlK>Lm{!JD zfTQ4N!M_6$Rj{ez{N@k9Qz-um)V}@=6n`$Oy82In*HHcpsQLdJDEa>qJR6+0>g` zp!j$dcoujYI0GC4Uk~07E(1RUo&)|2ycm4xn9D1>z%wZSJ5Y4o@5={3_4^X|YVcW5 z{^GabH1PcOPQNb(Mc3V+#%}?|-zP!Q`Cag};7`F|Mm8)(e_rD~E%ba@3hCl%qm9SIMo5 z!b~9>4p5m5i-mAF8Xqf`vgNLDRWZzGDvXKNX9g?b=-Lu}%Co|1ISQx0t6Ho?<>_7F zYD$?xm?>AHQm#B4UUvD+a0r^lawFNnOew3i3&n7-n9oOr3I*EA)qz69b5EGdXVye) zZggZkm@!za7AiARb-AcapV4Buk~FHI%H|;|sq?}?alnHsu8V?&*?g{$D_2Sx*E%v1 z4iq7c4kIJQv9PkX6lJnv)<6^vRP&=zHdt5)qf!Z4*JjFLad5C&in3kQR&t%?(M&#A zI64|-Mj#_o)`G57$>mwC6ouo(Dx35VW0^ullbe8G7R%c%!hvy?Mj*@j!b~=cgkf%G zuv9FUt&^j1!3&!3`9~WCQlOLTdct0LbV7SCm7|fN6ZI^z@l53eJ<6j+NGdu0IO-N< z3IYFxmxPO@>5&yOj`4+J1*NMNvK^_~vOSL=cs&#%*9b09nO5n5`>R5BJXnP6LzF)% z1=nW^msU^)v=}0yaAh=z`u0itRL)1UP?o`rY=I(@&hQ_4?u6!bX(+;cq&Lha5{siz z!Hq75*<2|atQ1S*b3*zI4u_*rDW5Bs(cdxy)@BMA#%QKomNf>~kL2ir5{;IMgAtZm zDF)X&lL8Avxiw*w%`u%XRlTmewrVL?SmPG%4QG^HpHd!WGlQU0C0D3M)K}TAD9uzW z#m-_W$DW-%cBC4IaySeuqNLV!QAsp+gqK}!+vvixRM7D#J2N!|${$5xaY%HAt5+_MAJ^rwQPC`lIc(=)lpipzid>C1z8dGl%n*Jk z9K=DjjL8H?+O-Y%4=-_o+=qR zOXnVXLsdk<;%I;fZ-0Bkg?-m2kBP(ZZBCDz1Bg5I-@2Sf(`g5x^vqW^`-^3LQ&dlO z9C1fkPx4$WMF=b|8WR>rGFz(3gUHAfVsWl4*BxdCq~AE#VhM8MO8i&4q1?ug1&fQ4 zi(*fh9}kB{GB79B$t5VN7lu9NGo|6`D8Y;nlEFBwQXbMh#X@N5V6-pEXL2LK5~)M5 zMDE)Cr7Izb;H6-&Wc?_nM!R7-SjrHmM$~F@i$NVt_2t~VqoiK^Tgso_V5#<@Kg=>x zEM{@(6^uB_&M}E$)5bUH=F(CzAKZXH$P5k(G2KdWG`OKutd0iD76%Bj;r3IHvOWS? zqiuFRQyvbMS)3GEJi?oDZh(-_r1>HN+y$^XvK+h+5v5o_i;D%KnS5MV%NTr!L_Rxf zl2<%0(Z!i{*c97%#^>!6D(O01ae2rQ5XNa~HDn$dMDNBD|dg=-N-hsiN$!X=i&awVrg9u=~9 zieP!Bv?k(jxf1p`>$T@NrLnl-n!V4pW|zb)j|yulYlG!QG(~^BIC)+}g2&%twGu3^ zmWIP}5q{*cVYUNR#0RW|<-39v%T_E2E90X|1ErG4BYazbKdBo#!rU%fwq#~XTQx4N ztt;e!eHtY9&#Z&P<3YMQsVXRAGuq3hQ(EYTjj|QdM*(pIKji_Y6D)N4PoC3<+Ap;(sEQ)t}2e^2CZ(ucXN?JZL(f#5{K=<{1i=P zO6#dA@{bKDo7ajMDStGm2+t!o(=8dHZ% zqLY$kmyi$Pr_gjoVxN_(L#|j=&)ho2^}JV?KIhVcTdNVSMC1*Wu;i{_C5Ib4@k$@b zgOx5}UrD?swWBE1im|V23eXfRxgwRPtNB{V+|g`*aSI$;Tn%TQ%G%IQMygQIbl~y} z|NO3MM5^TqxRrr%o-5UoW)+BQ*(BRgYS1tL<@{ASaK|_g{|`=cb7zmvpF4_!7$1q| zO&=n2>@+*RZnQFoK#s@iy=HX%bQ!uiUlMdek!e6t>L+1h@g!+>I@Mu;5Tc`LRPGJu zk|&~8^K;p`v;E_&q|#|cn+A@P@5JY&T6Cg~=TZKLr@0XWeWhy+~dUS=i`NOad z-*ytXilMV2 zJMV_e%;<2ZQP59h8wvVRQmJ6SKW{0|NeY&9BqjOjpudtC8q(hiM%eG7M89Kyuu3ry zF9VTf#5xE$TGdWQQh+kN>xz3hN9Tt)$p))96KV@#wiuO_KSt|0yotZwf#HiK9wt#w z5fnCgp3DddeU|)PMVaHoU8g@Mw4l$yFN4DWKwE0m(01h4q{^>E3*zpDSpOtLVm0)8A;kEZ`ZZ2*S>oT^Dfj8{*Yfzkl~q&qCCbQclkxWFJQvrNqr z^>8GY&k>fNr!RZPM>h8zAzMXJm2aD~SCZlSVV=(HxDiXHE{{foIrN-!69#T6iCL;T zRPzG`o#j^P3T+kfJD(|xhl4rt#yOVerqk6%yDrN*otqiS6^2jL*S8Nf9o$%&q&ZmK zzhq^2+rs{E(ZZW=Ua~k`ddtdi)s4&g!@gU1fwj6Ir{oSgWJRkB{)&UA;X-k&5Tmmn zava^PauG|tI0Ppo4@z995FpWY*#?PDN4q-XX5-9~uX4V$Kr{ots(ggIS;ldDR9sUa zg{D!CP*K=2d~T5nE??%RWo;2Zvbs>Njw&6A`=VI+Ooh9Pbc<64*Tk4nyAxT>LCy3! z;0f~`bm@)?3WdR2x%3^%4Z4#y1?^jv<6+t2%UhNIEhNy`0}&^kOIr4+Yg=oya}6mz zt}xbndE2hwwhRZDOgOey+w=z?moiB2?btXgBtmD(sM>AHACkkOJn!u+AO&?s1q))6 zB67M03>yLKf-szO*PNuzINuTadjmz1v;=HQMp|O6uxe{*9bf6D>`JC{aoVNe$}_5_ zRrmw#5~J20b%E^6DY@(N-KwrzV>-D2ybO$vEJL4=L=yf8!($Sf2!>i z3xf#6_cS)S%i4^^c)RG6XSdUDyMBqB{LO%7%I56C&n^scgSc)t($_m1CCZF57(Gxf zZ5M0L*URm~_p-Yyv%toktWD*vtz9o`haU=`QYr*$mj^s~=Vb;}?l2BC?AfV>Wo0cb z4NI?1@)W5x$usfSwkE+jcI5>eC*6%?w>9J+=zLp7HG4Sng)UEb@|y zah{8HWgN2oNH^Ar&+>f}mL|MwI3e-Vdx|lo2(bXoNs%)p#~Mrg+V#{`pp-W?Jn=M_ z;u4KSNF6gr=QAWF3ht7}9pk0V*q9Mq3LE!>*svyVsX|QLrrP;J7aP`r6Y-dwcx)@S zX=P`*!f3UEf8~Y@PfKE`w*VrlWgs{7O7G(|RQVhU){U!H^&!|Hc!RUHl?FN4TZ|+B zgNVKx`@-@#+2DG2Y0kUFHg|x7MP4Xy)={{jgT`dWITN}ml@&+GS~`ec^Ll~^*6!j} zF~+5D0cooK)25U*WupSQM^~GwP}0ex$SE(?Y95(Nz&R`7ddvpY)*E_uoZ$T^bR$;-QVe?LFrM&~L{YOwoloi{`U z-Og%sUr{y?_H

    b=@?5rBL5c+h03S-&8wXzrVh*Hc{Ug);G0ps2!ZLslKDOM~#=& z_A$|xu(qdmkjIJIVP-#CJ4#jkeH0H-F~R2r)a|bwVYTfckH@V0QPn?EJ669BYM#^- z`O|BUO(oDXu5||J)D_k~&G0X1{-Yw1AqTb2F^W%$JyaaktnZ^RL9c_e-2Ui7%|T71 z^`Tm0_lMK9$=Y$IILN0Lwe5~0+U-5`vz;B({A~v#iybr^V~bDLH|ax-F4!V!j);{k zFj3#Y`fz+7D{F%gS34x;P1L@?25ZOFrtZwMzxIR}9>3xc2cQg^;ZyB^_RQAlH$guy zDRHsQ0Aua%HiRY6ir;aawoA9E)Z~q7;|DvF9K@OXr*7IYWfL4Y6x6mOfGrMo2DPn- z0fj(=j`El0t>P_f!1Y*#O!rL7gW5JKcbKNt?~?`uwcSS25ei35T&U>#8GlHce-Is{ z;AN&-xIaMy78BGSleDEw%!&SMcgTi{CuI%7qa?eu=A z-pgQ;AxG98b*pb~@MxmGc>#=TtSVmr9@l*#lh@xbrAVCD3z+J67z*q5W6JPYBA4Fb zbj0J6=aT5gx{*Eh?FRo21h5`LVJJ@3WZS|9T)o_`sFO#>(J=W=l<*6baB;XoKXQud zQ0m`^5q@*KCM#^%JSEIfERlXB{u*7Dq8{rAY9})RuA%mrS5WiDO*At-?WwIeCiwZ= z*0c2wrMK;9#DQQeasd;KM-w2<0}I#~h zPBvI09%&PKXY%zE2CJB)TC-d+OrAxN@j%F$c&3I4iVgLhDmTrHi(zre$(cFMLcDjb zR*})$y?l&wR9L#rZxqQVS**m7(0RWGvS0HZ3PT698EuMnb~=|D&xk)W%_S!ZY7fz8 z0tJ-AB^PkMNbx$J*GFvTG)_C&2DL|7QiRKY!)PDUiQ+qIdvV0XJh|RFKK>!-INE4% z+8c{;Qe%C4l9FgMVyk_Bf%{eD*~qrE-+l3B_OZwV&N&~WRz~%NYirNn2^eLhP=F%Xi z5pk;Lbw!?DF-;n$kIULq$=0M3N?wd!h$IY6z)_`OWID0t?XMj}2c*KLn{-mzrm(q} z|IjC|rkKbkMklW5WYrlF$!@hE#Nd(?DVbA(Rn8`7gA-yp#b;ONEx}S*4NT@}(+Oi| z7UP#E?q2x_cw};Upv_w5kr+hw>GD@GR7#vuMZ&G7#j)yRIP{G!vU+StsxKKED;)J| zPh}HwvzFSXWYsHcGRxNTno245K9tvUV2Wz@XHPl=`n?3NzhF>#I9uf*xEkgyi!VUxpBt0SH@>qr1*~NU=m*JG7G7q!uMgwm^6v! zViL?xcCOf6m|NbCYf{RXH~?+goErq8&oHB5+9BqDOukHQ&Un~iv?iKh_sgSEpP zHzlw8APkqXtd>$V!N;t$7i`_ z4($z=w>fmSj|q;;(q5BQit&W=_OpDP#mGs6nt#BHJ!0Y#w-u($DWt1RPw)hUlsy=N z<&sMH52Gdf!tZU^uGs6&%t~Pri?NPG0W$!%ii`X{LgPv}!#%8%m}G*JXKhQhUU3m-A0#(5fy_2#OF1b^))03~lr(O~ zQ9+567pD&0E>x(}$xsv_Of=YKXJ}~JaX~2br8eL+a8efqR4s%lk-a*rh&>F#oMM!n zPA1)~cy6TJr%|CLoN7q<6pFc#gCqOJ%8t;ewj@ps098oIY){)RYoAK@{38j^{L$1H?>B8E_(%NQsY7_PQq&l3)pb_GX@$g@9 zFSbBLq3kC%)RP&LRZph8-@E2dBWP3X# z*a1M3stR2)*ClNdJD;#aum(ti4`>PXz`3}2O>)itpU<*8Wq>joXXA zNS+?PDA{P~wcLxF%K~s> zP))UM!dIgm)~leq^Nl#@~{f{TXSfNr;tUQnBhvQ&7C4JrBTN7&1MzSBd+I~Mdzv}3(syiSu05~ zrB2?*%3u!3R-{Dilv@J0U;&<#nMXTh_L7I1_{}|W_u{;EBks!aNdx0{Kfh|?Ydu?m z^r+z$l%pliPO3^6(bU*@{LZg2C4zAN)?S=AdR+iYIc{e&&JNvslr|zkV2(@|W3@v(ASK zo9-QC%r$vQ?FyFjxEI~&#?5T#^}GJbaKuZfuxq4~QwG8o^A3hQ;ASOe!%^+D|u(B)3> zNpESNZnq?UW1+@1omAUa8y}_7;8+&C zK4~!X#7kj`&-2S36u{E?+C=h0oyO08M2j-Wq{$XZe%^zD*pV}7?n-@i(T;w58-wEz zWlHCbU$#wW*$PGOYYW1e-y#<89Fe&vI|eA+xuD5+BRubOKM;z^>2;O@=k9mE5~7RZ zw|*%U@2Gcr?ek`FIzBXb5dUbXwwvtO*}rj-_onKFVBBR26u3B+N>*flX<5NH*(u+m zxJc3TIgUH4B*Js%OYV*KScr9@Ipw!tgR)XZR-&67?(~I>KS7}fKGd`%)XjG&7N^}> zcGv2Mv&F_GkN4aJ&MBOwJW6_WyTlYdNljVf{f5FZJapS&0{5~<+PWo`=@XanmO8!I zO@Jj#`xRo8^(f(U`4-lKfmqaPexdEpB$MA{YYs8Vzu0!NY;)Imf;mw+!lukGHIazb zt(aV-^Y$B9N~S1s>-WHH86kpm$3l0(L9h&Jk2~8kalmwqc9$clmY22yfD3wdBB8;l zed}ozZg7#MfWcQ({>v(~ZoAVN>1x|?LG6=XN-l3ZUI(fX1`ZMC#wySbF9X|HjlBCQs>eIT#P$_trG~w)g;@+6@erQN6$+&L8Ss zGR($0sW1v>~c|&TpActGnp&K*T$T3YC;*KSXe_I@3rbkSnkJ{XUBff$Vk1bV_ z722IDYwVY<{dHn(oKBJ;n-9H8udJ^IrbtM#RyN+KW|-s_AJC0k zW)Ae)p7VL(%^?+p-w~bK>>|wi7dLJw9~p24lm|- z%+x3kizon2zOO?Yo!3UMj3Av9xuIe&ZB~?OCG!0JuJ*9(M5%|fL%YnN;uzVUC9VoR zO6s^4aSAIsB@jB{$CJC+ghW$$ceCyj3A4?Fr!eHGMkWz}Eq{zfWHO4Y4+fhffgcVIWh*_tSa;5O6UJ@^7gZE zdxBELFT2=_e_;s&9%sqpx{BJyBs)t(&E8=* zMk(<&nCsQlz5aF%%UPy3q*S)4(4o)j^$9120O|YKQ8N#WR(+q1X^gB#OJ}BK>yRsof@F*;0B@V$O$VM~`WYNdp3L*K)`hiZ>92d}R^lil%r)kz8(1 z*>1j?H7u56f%pm$TZ-qN-$X2Q z?L(CK06vu4VI%B}9D?ahxwKDrZZ_RHrmuf!KCZ1cUYD6RnjAwH&818BhEs~HSd&gC zO`W+RqS`K!ExUc3E>D_!fF+I$+I0(s70L4=izffl5Sy8y_~F}4;pA|RxgX^HUM!XO z>c%Kjcw0d!A^NX%6#P}>?LZr2woqo4qUhChqO+}gM`AtNZUZ50XQaO=uWxBsn0Jop zou_O`th3mu^FSN%JJn%HO`|UWECPpPR! zBP$utl_G36J!*Tf=z>`;ot-`4wZZTtq6ATe7MQeP9E|^vNJ`#Bnw0gJEf702ph*KrjWOnZ5|?ind|Y1ub4$cDw&L>*`(hs zT8j{#_xd3*O1T63$Y}}#mie^_Kk3mpl9TM~6RJm0O&vW_*`d@=X@+HnQl7Ne z^*OeWA^Os1qUCE;PXJdz%?TVxN~64|myZhT{Kkt}khTkFwn8HzV+{_8o>nQQPltGke*%8eFjc`EU=jcle zzC<$67nu@ODx*nP7|#|2OyPdd=6{{Qo^;Gj{@*NYkMPd~&efrPZ!g{L2vjQzkc{fv z1D*Gn&3}0zi!}D^G4q$)os5#?(I>{q2;LN@rj#r+LUQ&BE!{9P(+}wNHKvS&{Li^t zsm2}zY+f5?I$Hd%O_|9ohFl+h_zy!G1@2IjLVygFSnCGFHl}@OQn#k5omr84V&@j| zG_6^e8J=CuiIL{ZQm5_!rI%2|Hu=-0BKn$Y4J6Ch%TTcWP0TU2@;v#q4bIWN@otg> zMiAF(U)rW_`b{dD)L-U1ei0Fg-TqrZB=E4QnH;G10pU}hZTGp!Q-?{dbeTXBEx*w? zt?820B;j2yCLkdvmsny!y4c(r$YhXdtlz#U9dA5J&!uV1, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-10-18 13:47+0200\n" +"PO-Revision-Date: 2016-06-19 17:31+0300\n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Last-Translator: kotobenko \n" +"Language-Team: \n" +"X-Generator: Poedit 1.8.8\n" + +#: ../../../../inc/functions.php:583 ../../../../inc/functions.php:600 +#: ../../../../inc/functions.php:591 ../../../../inc/functions.php:608 +#: ../../../../inc/functions.php:620 ../../../../inc/functions.php:637 +#: ../../../../inc/functions.php:623 ../../../../inc/functions.php:640 +#: ../../../../inc/functions.php:629 ../../../../inc/functions.php:646 +#: ../../../../inc/functions.php:643 ../../../../inc/functions.php:660 +#: ../../../../inc/functions.php:653 ../../../../inc/functions.php:670 +#: ../../../../inc/functions.php:655 ../../../../inc/functions.php:672 +#: ../../../../inc/functions.php:683 ../../../../inc/functions.php:700 +#: ../../../../inc/functions.php:701 ../../../../inc/functions.php:719 +msgid "second" +msgid_plural "seconds" +msgstr[0] "секунда" +msgstr[1] "секунди" +msgstr[2] "секунд" + +#. 60*60 = 3600 +#: ../../../../inc/functions.php:585 ../../../../inc/functions.php:602 +#: ../../../../inc/functions.php:593 ../../../../inc/functions.php:610 +#: ../../../../inc/functions.php:622 ../../../../inc/functions.php:639 +#: ../../../../inc/functions.php:625 ../../../../inc/functions.php:642 +#: ../../../../inc/functions.php:631 ../../../../inc/functions.php:648 +#: ../../../../inc/functions.php:645 ../../../../inc/functions.php:662 +#: ../../../../inc/functions.php:655 ../../../../inc/functions.php:672 +#: ../../../../inc/functions.php:657 ../../../../inc/functions.php:674 +#: ../../../../inc/functions.php:685 ../../../../inc/functions.php:702 +#: ../../../../inc/functions.php:703 ../../../../inc/functions.php:721 +msgid "minute" +msgid_plural "minutes" +msgstr[0] "хвилина" +msgstr[1] "хвилини" +msgstr[2] "хвилин" + +#. 60*60*24 = 86400 +#: ../../../../inc/functions.php:587 ../../../../inc/functions.php:604 +#: ../../../../inc/functions.php:595 ../../../../inc/functions.php:612 +#: ../../../../inc/functions.php:624 ../../../../inc/functions.php:641 +#: ../../../../inc/functions.php:627 ../../../../inc/functions.php:644 +#: ../../../../inc/functions.php:633 ../../../../inc/functions.php:650 +#: ../../../../inc/functions.php:647 ../../../../inc/functions.php:664 +#: ../../../../inc/functions.php:657 ../../../../inc/functions.php:674 +#: ../../../../inc/functions.php:659 ../../../../inc/functions.php:676 +#: ../../../../inc/functions.php:687 ../../../../inc/functions.php:704 +#: ../../../../inc/functions.php:705 ../../../../inc/functions.php:723 +msgid "hour" +msgid_plural "hours" +msgstr[0] "година" +msgstr[1] "години" +msgstr[2] "годин" + +#. 60*60*24*7 = 604800 +#: ../../../../inc/functions.php:589 ../../../../inc/functions.php:606 +#: ../../../../inc/functions.php:597 ../../../../inc/functions.php:614 +#: ../../../../inc/functions.php:626 ../../../../inc/functions.php:643 +#: ../../../../inc/functions.php:629 ../../../../inc/functions.php:646 +#: ../../../../inc/functions.php:635 ../../../../inc/functions.php:652 +#: ../../../../inc/functions.php:649 ../../../../inc/functions.php:666 +#: ../../../../inc/functions.php:659 ../../../../inc/functions.php:676 +#: ../../../../inc/functions.php:661 ../../../../inc/functions.php:678 +#: ../../../../inc/functions.php:689 ../../../../inc/functions.php:706 +#: ../../../../inc/functions.php:707 ../../../../inc/functions.php:725 +msgid "day" +msgid_plural "days" +msgstr[0] "день" +msgstr[1] "дні" +msgstr[2] "днів" + +#. 60*60*24*365 = 31536000 +#: ../../../../inc/functions.php:591 ../../../../inc/functions.php:608 +#: ../../../../inc/functions.php:599 ../../../../inc/functions.php:616 +#: ../../../../inc/functions.php:628 ../../../../inc/functions.php:645 +#: ../../../../inc/functions.php:631 ../../../../inc/functions.php:648 +#: ../../../../inc/functions.php:637 ../../../../inc/functions.php:654 +#: ../../../../inc/functions.php:651 ../../../../inc/functions.php:668 +#: ../../../../inc/functions.php:661 ../../../../inc/functions.php:678 +#: ../../../../inc/functions.php:663 ../../../../inc/functions.php:680 +#: ../../../../inc/functions.php:691 ../../../../inc/functions.php:708 +#: ../../../../inc/functions.php:709 ../../../../inc/functions.php:727 +msgid "week" +msgid_plural "weeks" +msgstr[0] "тиждень" +msgstr[1] "тижні" +msgstr[2] "тижнів" + +#: ../../../../inc/functions.php:594 ../../../../inc/functions.php:611 +#: ../../../../inc/functions.php:602 ../../../../inc/functions.php:619 +#: ../../../../inc/functions.php:631 ../../../../inc/functions.php:648 +#: ../../../../inc/functions.php:634 ../../../../inc/functions.php:651 +#: ../../../../inc/functions.php:640 ../../../../inc/functions.php:657 +#: ../../../../inc/functions.php:654 ../../../../inc/functions.php:671 +#: ../../../../inc/functions.php:664 ../../../../inc/functions.php:681 +#: ../../../../inc/functions.php:666 ../../../../inc/functions.php:683 +#: ../../../../inc/functions.php:694 ../../../../inc/functions.php:711 +#: ../../../../inc/functions.php:729 +msgid "year" +msgid_plural "years" +msgstr[0] "рік" +msgstr[1] "роки" +msgstr[2] "років" + +#: ../../../../inc/functions.php:628 ../../../../inc/functions.php:670 +#: ../../../../inc/functions.php:699 ../../../../inc/functions.php:702 +#: ../../../../inc/functions.php:708 ../../../../inc/functions.php:722 +#: ../../../../inc/functions.php:732 ../../../../inc/functions.php:727 +#: ../../../../inc/functions.php:755 ../../../../inc/functions.php:774 +msgid "Banned!" +msgstr "Заблоковано!" + +#. There is no previous page. +#: ../../../../inc/functions.php:1125 ../../../../inc/functions.php:1139 +#: ../../../../inc/functions.php:1165 ../../../../inc/functions.php:1179 +#: ../../../../inc/functions.php:1168 ../../../../inc/functions.php:1182 +#: ../../../../inc/functions.php:1197 ../../../../inc/functions.php:1211 +#: ../../../../inc/functions.php:1200 ../../../../inc/functions.php:1214 +#: ../../../../inc/functions.php:1206 ../../../../inc/functions.php:1220 +#: ../../../../inc/functions.php:1234 ../../../../inc/functions.php:1230 +#: ../../../../inc/functions.php:1244 ../../../../inc/functions.php:1228 +#: ../../../../inc/functions.php:1242 ../../../../inc/functions.php:1256 +#: ../../../../inc/functions.php:1261 ../../../../inc/functions.php:1275 +msgid "Previous" +msgstr "Попередня" + +#. There is no next page. +#: ../../../../inc/functions.php:1144 ../../../../inc/functions.php:1153 +#: ../../../../inc/functions.php:1184 ../../../../inc/functions.php:1193 +#: ../../../../inc/functions.php:1187 ../../../../inc/functions.php:1196 +#: ../../../../inc/functions.php:1216 ../../../../inc/functions.php:1225 +#: ../../../../inc/functions.php:1219 ../../../../inc/functions.php:1228 +#: ../../../../inc/functions.php:1234 ../../../../inc/functions.php:1239 +#: ../../../../inc/functions.php:1248 ../../../../inc/functions.php:1249 +#: ../../../../inc/functions.php:1258 ../../../../inc/functions.php:1233 +#: ../../../../inc/functions.php:1242 ../../../../inc/functions.php:1261 +#: ../../../../inc/functions.php:1270 ../../../../inc/functions.php:1280 +#: ../../../../inc/functions.php:1289 +msgid "Next" +msgstr "Наступна" + +#: ../../../../inc/display.php:93 ../../../../inc/display.php:105 +#: ../../../../inc/display.php:108 ../../../../inc/display.php:112 +#: ../../../../inc/display.php:126 +msgid "Error" +msgstr "Помилка" + +#: ../../../../inc/display.php:94 ../../../../inc/display.php:106 +#: ../../../../inc/display.php:109 ../../../../inc/display.php:113 +#: ../../../../inc/display.php:127 +msgid "An error has occured." +msgstr "Сталася помилка." + +#: ../../../../inc/display.php:110 ../../../../inc/mod/pages.php:62 +#: ../../../../inc/mod/pages.php:60 ../../../../inc/display.php:122 +#: ../../../../inc/display.php:125 ../../../../inc/display.php:129 +#: ../../../../inc/display.php:143 +msgid "Login" +msgstr "Логін" + +#: ../../../../inc/display.php:229 ../../../../inc/display.php:241 +#: ../../../../inc/display.php:244 ../../../../inc/display.php:248 +#: ../../../../inc/display.php:262 +#, php-format +msgid "Post too long. Click here to view the full text." +msgstr "" +"Допис задовгий. Натисніть тут, щоб переглянути текст " +"повністю." + +#: ../../../../inc/display.php:368 ../../../../inc/display.php:473 +#: ../../../../inc/display.php:385 ../../../../inc/display.php:495 +#: ../../../../inc/display.php:388 ../../../../inc/display.php:498 +#: ../../../../inc/display.php:392 ../../../../inc/display.php:502 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:60 +msgid "Ban" +msgstr "Заблокувати" + +#: ../../../../inc/display.php:372 ../../../../inc/display.php:477 +#: ../../../../inc/display.php:389 ../../../../inc/display.php:499 +#: ../../../../inc/display.php:392 ../../../../inc/display.php:502 +#: ../../../../inc/display.php:396 ../../../../inc/display.php:506 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:74 +msgid "Ban & Delete" +msgstr "Заблокувати й видалити" + +#. line 4 +#: ../../../../inc/display.php:376 ../../../../inc/display.php:481 +#: ../../../../inc/display.php:393 ../../../../inc/display.php:503 +#: ../../../../inc/display.php:396 ../../../../inc/display.php:506 +#: ../../../../inc/display.php:400 ../../../../inc/display.php:510 +#: ../../../../templates/cache/b9/2b/ba2b45df5e1d76f6cdfb98a47468df19a1ffc1c2af2dc1792eb75eeb0791.php:29 +msgid "Delete file" +msgstr "Видалити файл" + +#: ../../../../inc/display.php:376 ../../../../inc/display.php:481 +#: ../../../../inc/display.php:393 ../../../../inc/display.php:503 +#: ../../../../inc/display.php:396 ../../../../inc/display.php:506 +#: ../../../../inc/display.php:400 ../../../../inc/display.php:510 +#: ../../../../templates/cache/b9/2b/ba2b45df5e1d76f6cdfb98a47468df19a1ffc1c2af2dc1792eb75eeb0791.php:29 +msgid "Are you sure you want to delete this file?" +msgstr "Ви впевнені, що хочете видалити цей файл?" + +#: ../../../../inc/display.php:380 ../../../../inc/display.php:485 +#: ../../../../inc/display.php:397 ../../../../inc/display.php:507 +#: ../../../../inc/display.php:400 ../../../../inc/display.php:510 +#: ../../../../inc/display.php:404 ../../../../inc/display.php:514 +msgid "Spoiler File" +msgstr "Заховати файл у спойлер" + +#: ../../../../inc/display.php:380 ../../../../inc/display.php:485 +#: ../../../../inc/display.php:397 ../../../../inc/display.php:507 +#: ../../../../inc/display.php:400 ../../../../inc/display.php:510 +#: ../../../../inc/display.php:404 ../../../../inc/display.php:514 +#: ../../../../templates/cache/b9/2b/ba2b45df5e1d76f6cdfb98a47468df19a1ffc1c2af2dc1792eb75eeb0791.php:36 +msgid "Are you sure you want to spoiler this file?" +msgstr "Ви впевнені, що хочете заховати цей файл у спойлер?" + +# ОП з усіма дописами чи тільки один допис? +#: ../../../../inc/display.php:384 ../../../../inc/display.php:401 +#: ../../../../inc/display.php:404 ../../../../inc/display.php:408 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:193 +msgid "Move reply to another board" +msgstr "Перемістити відповідь до іншої дошки" + +#: ../../../../inc/display.php:388 ../../../../inc/display.php:512 +#: ../../../../inc/mod/pages.php:1425 ../../../../inc/mod/pages.php:1494 +#: ../../../../inc/display.php:405 ../../../../inc/display.php:534 +#: ../../../../inc/display.php:408 ../../../../inc/display.php:537 +#: ../../../../inc/display.php:412 ../../../../inc/display.php:541 +#: ../../../../inc/mod/pages.php:1508 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:208 +#: ../../../../inc/mod/pages.php:1509 ../../../../inc/mod/pages.php:1506 +#: ../../../../inc/mod/pages.php:1511 +msgid "Edit post" +msgstr "Редагувати допис" + +#. line 5 +#. line 6 +#: ../../../../inc/display.php:461 +#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:33 +#: ../../../../inc/display.php:483 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:36 +#: ../../../../inc/display.php:486 ../../../../inc/display.php:490 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:38 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:36 +msgid "Delete" +msgstr "Видалити" + +#: ../../../../inc/display.php:461 ../../../../inc/display.php:483 +#: ../../../../inc/display.php:486 ../../../../inc/display.php:490 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:36 +msgid "Are you sure you want to delete this?" +msgstr "Ви впевнені, що хочете видалити?" + +#: ../../../../inc/display.php:465 ../../../../inc/display.php:487 +#: ../../../../inc/display.php:490 ../../../../inc/display.php:494 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:44 +msgid "Delete all posts by IP" +msgstr "Видалити всі дописи цього IP" + +#: ../../../../inc/display.php:465 ../../../../inc/display.php:487 +#: ../../../../inc/display.php:490 ../../../../inc/display.php:494 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:44 +msgid "Are you sure you want to delete all posts by this IP address?" +msgstr "Ви впевнені, що хочете видалити всі дописи цього IP?" + +#: ../../../../inc/display.php:469 ../../../../inc/display.php:491 +#: ../../../../inc/display.php:494 ../../../../inc/display.php:498 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:52 +msgid "Delete all posts by IP across all boards" +msgstr "Видалити всі дописи цього IP на всіх дошках" + +#: ../../../../inc/display.php:469 ../../../../inc/display.php:491 +#: ../../../../inc/display.php:494 ../../../../inc/display.php:498 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:52 +msgid "" +"Are you sure you want to delete all posts by this IP address, across all " +"boards?" +msgstr "Ви впенені, що хочете видалити всі дописи цього IP на всіх дошках?" + +#: ../../../../inc/display.php:490 ../../../../inc/display.php:512 +#: ../../../../inc/display.php:515 ../../../../inc/display.php:519 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:96 +msgid "Make thread not sticky" +msgstr "Відкріпити нитку" + +#: ../../../../inc/display.php:492 ../../../../inc/display.php:514 +#: ../../../../inc/display.php:517 ../../../../inc/display.php:521 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:106 +msgid "Make thread sticky" +msgstr "Закріпити нитку" + +#: ../../../../inc/display.php:496 ../../../../inc/display.php:518 +#: ../../../../inc/display.php:521 ../../../../inc/display.php:525 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:122 +msgid "Allow thread to be bumped" +msgstr "Увімкнути цій нитці підіймання" + +#: ../../../../inc/display.php:498 ../../../../inc/display.php:520 +#: ../../../../inc/display.php:523 ../../../../inc/display.php:527 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:132 +msgid "Prevent thread from being bumped" +msgstr "Вимкнути цій нитці підіймання" + +#: ../../../../inc/display.php:503 ../../../../inc/display.php:525 +#: ../../../../inc/display.php:528 ../../../../inc/display.php:532 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:148 +msgid "Unlock thread" +msgstr "Відкрити нитку" + +#: ../../../../inc/display.php:505 ../../../../inc/display.php:527 +#: ../../../../inc/display.php:530 ../../../../inc/display.php:534 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:158 +msgid "Lock thread" +msgstr "Закрити нитку" + +#: ../../../../inc/display.php:508 ../../../../inc/display.php:530 +#: ../../../../inc/display.php:533 ../../../../inc/display.php:537 +#: ../../../../templates/cache/59/eb/57ea544c6cb07c523441e4872c03216560589e8425c0df3a895c17253c4c.php:181 +msgid "Move thread to another board" +msgstr "Перемістити нитку на іншу дошку" + +#. How long before Tinyboard forgets about a mute? +#. 2 weeks +#. If you want to alter the algorithm a bit. Default value is 2. +#. (n^x where x is the number of previous mutes) +#: ../../../../inc/config.php:346 ../../../../inc/config.php:473 +#: ../../../../inc/config.php:474 ../../../../inc/config.php:475 +#: ../../../../inc/config.php:476 ../../../../inc/config.php:479 +#: ../../../../inc/config.php:482 +msgid "You have been muted for unoriginal content." +msgstr "Вас позбавлено права дописувати за неоригінальний контент." + +#. The names on the post buttons. (On most imageboards, these are both just +#. "Post"). +#. The names on the post buttons. (On most imageboards, these are both just "Post"). +#: ../../../../inc/config.php:677 ../../../../inc/config.php:781 +#: ../../../../inc/config.php:772 ../../../../inc/config.php:774 +#: ../../../../inc/config.php:776 ../../../../inc/config.php:792 +#: ../../../../inc/config.php:803 ../../../../inc/config.php:829 +#: ../../../../inc/config.php:832 ../../../../inc/config.php:835 +msgid "New Topic" +msgstr "Нова тема" + +#: ../../../../inc/config.php:678 ../../../../inc/config.php:782 +#: ../../../../inc/config.php:773 ../../../../inc/config.php:775 +#: ../../../../inc/config.php:777 ../../../../inc/config.php:793 +#: ../../../../inc/config.php:804 ../../../../inc/config.php:830 +#: ../../../../inc/config.php:833 ../../../../inc/config.php:836 +msgid "New Reply" +msgstr "Нова відповідь" + +#. Additional lines added to the footer of all pages. +#: ../../../../inc/config.php:689 ../../../../inc/config.php:793 +#: ../../../../inc/config.php:784 ../../../../inc/config.php:786 +#: ../../../../inc/config.php:788 ../../../../inc/config.php:804 +#: ../../../../inc/config.php:815 ../../../../inc/config.php:841 +#: ../../../../inc/config.php:844 ../../../../inc/config.php:847 +msgid "" +"All trademarks, copyrights, comments, and images on this page are owned by " +"and are the responsibility of their respective parties." +msgstr "" + +#. * ==================== +#. * Error messages +#. * ==================== +#. Error messages +#: ../../../../inc/config.php:866 +msgid "Lurk some more before posting." +msgstr "" +"Вам не завадило б почитати Українську Драматику, перш ніж щось сюди писати." + +#. * ==================== +#. * Error messages +#. * ==================== +#. Error messages +#. +#. * ==================== +#. * Error messages +#. * ==================== +#. +#. Error messages +#: ../../../../inc/config.php:867 ../../../../inc/config.php:972 +#: ../../../../inc/config.php:963 ../../../../inc/config.php:965 +#: ../../../../inc/config.php:967 ../../../../inc/config.php:983 +#: ../../../../inc/config.php:991 ../../../../inc/config.php:1017 +#: ../../../../inc/config.php:1020 ../../../../inc/config.php:1023 +msgid "You look like a bot." +msgstr "Ви поводитесь як бот." + +#: ../../../../inc/config.php:868 ../../../../inc/config.php:973 +#: ../../../../inc/config.php:964 ../../../../inc/config.php:966 +#: ../../../../inc/config.php:968 ../../../../inc/config.php:984 +#: ../../../../inc/config.php:992 ../../../../inc/config.php:1018 +#: ../../../../inc/config.php:1021 ../../../../inc/config.php:1024 +msgid "Your browser sent an invalid or no HTTP referer." +msgstr "Ваш браузер не надіслав HTTP реферера або надіслав хибний" + +#: ../../../../inc/config.php:869 ../../../../inc/config.php:974 +#: ../../../../inc/config.php:965 ../../../../inc/config.php:967 +#: ../../../../inc/config.php:969 ../../../../inc/config.php:985 +#: ../../../../inc/config.php:993 ../../../../inc/config.php:1019 +#: ../../../../inc/config.php:1022 ../../../../inc/config.php:1025 +#, php-format +msgid "The %s field was too long." +msgstr "Поле %s занадто довге." + +#: ../../../../inc/config.php:870 ../../../../inc/config.php:975 +#: ../../../../inc/config.php:966 ../../../../inc/config.php:968 +#: ../../../../inc/config.php:970 ../../../../inc/config.php:986 +#: ../../../../inc/config.php:994 ../../../../inc/config.php:1020 +#: ../../../../inc/config.php:1023 ../../../../inc/config.php:1026 +msgid "The body was too long." +msgstr "Вміст допису занадто довге." + +#: ../../../../inc/config.php:871 ../../../../inc/config.php:976 +#: ../../../../inc/config.php:967 ../../../../inc/config.php:969 +#: ../../../../inc/config.php:971 ../../../../inc/config.php:987 +#: ../../../../inc/config.php:995 ../../../../inc/config.php:1021 +#: ../../../../inc/config.php:1024 ../../../../inc/config.php:1027 +msgid "The body was too short or empty." +msgstr "Вміст допису занадто короткий або пустий." + +#: ../../../../inc/config.php:872 ../../../../inc/config.php:977 +#: ../../../../inc/config.php:968 ../../../../inc/config.php:970 +#: ../../../../inc/config.php:972 ../../../../inc/config.php:988 +#: ../../../../inc/config.php:996 ../../../../inc/config.php:1022 +#: ../../../../inc/config.php:1025 ../../../../inc/config.php:1028 +msgid "You must upload an image." +msgstr "Допис повинен містити зображення." + +#: ../../../../inc/config.php:873 ../../../../inc/config.php:978 +#: ../../../../inc/config.php:969 ../../../../inc/config.php:971 +#: ../../../../inc/config.php:973 ../../../../inc/config.php:989 +#: ../../../../inc/config.php:998 ../../../../inc/config.php:1024 +#: ../../../../inc/config.php:1027 ../../../../inc/config.php:1030 +msgid "The server failed to handle your upload." +msgstr "Не вдалося завантажити файл на сервер." + +#: ../../../../inc/config.php:874 ../../../../inc/config.php:979 +#: ../../../../inc/config.php:970 ../../../../inc/config.php:972 +#: ../../../../inc/config.php:974 ../../../../inc/config.php:990 +#: ../../../../inc/config.php:999 ../../../../inc/config.php:1025 +#: ../../../../inc/config.php:1028 ../../../../inc/config.php:1031 +msgid "Unsupported image format." +msgstr "Формат зображення не підтримується." + +#: ../../../../inc/config.php:875 ../../../../inc/config.php:980 +#: ../../../../inc/config.php:971 ../../../../inc/config.php:973 +#: ../../../../inc/config.php:975 ../../../../inc/config.php:991 +#: ../../../../inc/config.php:1000 ../../../../inc/config.php:1026 +#: ../../../../inc/config.php:1029 ../../../../inc/config.php:1032 +msgid "Invalid board!" +msgstr "Неправильна дошка!" + +#: ../../../../inc/config.php:876 ../../../../inc/config.php:981 +#: ../../../../inc/config.php:972 ../../../../inc/config.php:974 +#: ../../../../inc/config.php:976 ../../../../inc/config.php:992 +#: ../../../../inc/config.php:1001 ../../../../inc/config.php:1027 +#: ../../../../inc/config.php:1030 ../../../../inc/config.php:1033 +msgid "Thread specified does not exist." +msgstr "Вказана нитка не існує." + +#: ../../../../inc/config.php:877 ../../../../inc/config.php:982 +#: ../../../../inc/config.php:973 ../../../../inc/config.php:975 +#: ../../../../inc/config.php:977 ../../../../inc/config.php:993 +#: ../../../../inc/config.php:1002 ../../../../inc/config.php:1028 +#: ../../../../inc/config.php:1031 ../../../../inc/config.php:1034 +msgid "Thread locked. You may not reply at this time." +msgstr "Нитку закрито. Ви більше не можете сюди дописати." + +#: ../../../../inc/config.php:878 ../../../../inc/config.php:983 +#: ../../../../inc/config.php:974 ../../../../inc/config.php:976 +#: ../../../../inc/config.php:978 ../../../../inc/config.php:994 +#: ../../../../inc/config.php:1003 ../../../../inc/config.php:1029 +#: ../../../../inc/config.php:1032 ../../../../inc/config.php:1035 +msgid "Thread has reached its maximum reply limit." +msgstr "Кількість дописів у нитці досягла максимально припустимої." + +#: ../../../../inc/config.php:879 ../../../../inc/config.php:984 +#: ../../../../inc/config.php:975 ../../../../inc/config.php:977 +#: ../../../../inc/config.php:979 ../../../../inc/config.php:995 +#: ../../../../inc/config.php:1004 ../../../../inc/config.php:1030 +#: ../../../../inc/config.php:1033 ../../../../inc/config.php:1036 +msgid "Thread has reached its maximum image limit." +msgstr "Кількість зображень у нитці досягла максимально припустимої." + +#: ../../../../inc/config.php:880 ../../../../inc/config.php:985 +#: ../../../../inc/config.php:976 ../../../../inc/config.php:978 +#: ../../../../inc/config.php:980 ../../../../inc/config.php:996 +#: ../../../../inc/config.php:1005 ../../../../inc/config.php:1031 +#: ../../../../inc/config.php:1034 ../../../../inc/config.php:1037 +msgid "You didn't make a post." +msgstr "Ви не розмістили допис." + +#: ../../../../inc/config.php:881 ../../../../inc/config.php:986 +#: ../../../../inc/config.php:977 ../../../../inc/config.php:979 +#: ../../../../inc/config.php:981 ../../../../inc/config.php:997 +#: ../../../../inc/config.php:1006 ../../../../inc/config.php:1032 +#: ../../../../inc/config.php:1035 ../../../../inc/config.php:1038 +msgid "Flood detected; Post discarded." +msgstr "Виявлено флуд — опис відхилено." + +#: ../../../../inc/config.php:882 ../../../../inc/config.php:987 +#: ../../../../inc/config.php:978 ../../../../inc/config.php:980 +#: ../../../../inc/config.php:982 ../../../../inc/config.php:998 +#: ../../../../inc/config.php:1007 ../../../../inc/config.php:1033 +#: ../../../../inc/config.php:1036 ../../../../inc/config.php:1039 +msgid "Your request looks automated; Post discarded." +msgstr "Ваш запит схожий на автоматизований— допис відхилено." + +#: ../../../../inc/config.php:883 ../../../../inc/config.php:988 +#: ../../../../inc/config.php:979 ../../../../inc/config.php:981 +#: ../../../../inc/config.php:983 ../../../../inc/config.php:999 +#: ../../../../inc/config.php:1008 ../../../../inc/config.php:1034 +#: ../../../../inc/config.php:1037 ../../../../inc/config.php:1040 +msgid "Unoriginal content!" +msgstr "Неоригінальний контент!" + +#: ../../../../inc/config.php:884 ../../../../inc/config.php:989 +#: ../../../../inc/config.php:980 ../../../../inc/config.php:982 +#: ../../../../inc/config.php:984 ../../../../inc/config.php:1000 +#: ../../../../inc/config.php:1009 ../../../../inc/config.php:1035 +#: ../../../../inc/config.php:1038 ../../../../inc/config.php:1041 +#, php-format +msgid "Unoriginal content! You have been muted for %d seconds." +msgstr "" +"Неоригінальний контент! Вас позбавлено можливості дописувати на %d секунд." + +#: ../../../../inc/config.php:885 ../../../../inc/config.php:990 +#: ../../../../inc/config.php:981 ../../../../inc/config.php:983 +#: ../../../../inc/config.php:985 ../../../../inc/config.php:1001 +#: ../../../../inc/config.php:1010 ../../../../inc/config.php:1036 +#: ../../../../inc/config.php:1039 ../../../../inc/config.php:1042 +#, php-format +msgid "You are muted! Expires in %d seconds." +msgstr "Вас позбавлено можливості дописувати! Буде повернено через %d секунд." + +#: ../../../../inc/config.php:886 ../../../../inc/config.php:991 +#: ../../../../inc/config.php:982 ../../../../inc/config.php:984 +#: ../../../../inc/config.php:986 ../../../../inc/config.php:1002 +#: ../../../../inc/config.php:1011 ../../../../inc/config.php:1037 +#: ../../../../inc/config.php:1040 ../../../../inc/config.php:1043 +#, php-format +msgid "Your IP address is listed in %s." +msgstr "Вашу IP адресу знайдено в списку %s." + +#: ../../../../inc/config.php:887 ../../../../inc/config.php:992 +#: ../../../../inc/config.php:983 ../../../../inc/config.php:985 +#: ../../../../inc/config.php:987 ../../../../inc/config.php:1003 +#: ../../../../inc/config.php:1012 ../../../../inc/config.php:1038 +#: ../../../../inc/config.php:1041 ../../../../inc/config.php:1044 +msgid "Too many links; flood detected." +msgstr "Забагато посилань — виявлено флуд." + +#: ../../../../inc/config.php:888 ../../../../inc/config.php:993 +#: ../../../../inc/config.php:984 ../../../../inc/config.php:986 +#: ../../../../inc/config.php:988 ../../../../inc/config.php:1004 +#: ../../../../inc/config.php:1013 ../../../../inc/config.php:1039 +#: ../../../../inc/config.php:1042 ../../../../inc/config.php:1045 +msgid "Too many cites; post discarded." +msgstr "Забагато цитування — допис відхилено." + +#: ../../../../inc/config.php:889 ../../../../inc/config.php:994 +#: ../../../../inc/config.php:985 ../../../../inc/config.php:987 +#: ../../../../inc/config.php:989 ../../../../inc/config.php:1005 +#: ../../../../inc/config.php:1014 ../../../../inc/config.php:1040 +#: ../../../../inc/config.php:1043 ../../../../inc/config.php:1046 +msgid "Too many cross-board links; post discarded." +msgstr "Забагато міждошкових посилань — допис відхилено." + +#: ../../../../inc/config.php:890 ../../../../inc/config.php:995 +#: ../../../../inc/config.php:986 ../../../../inc/config.php:988 +#: ../../../../inc/config.php:990 ../../../../inc/config.php:1006 +#: ../../../../inc/config.php:1015 ../../../../inc/config.php:1041 +#: ../../../../inc/config.php:1044 ../../../../inc/config.php:1047 +msgid "You didn't select anything to delete." +msgstr "Ви не вибрали жодного допису, щоб видалити." + +#: ../../../../inc/config.php:891 ../../../../inc/config.php:996 +#: ../../../../inc/config.php:987 ../../../../inc/config.php:989 +#: ../../../../inc/config.php:991 ../../../../inc/config.php:1007 +#: ../../../../inc/config.php:1016 ../../../../inc/config.php:1042 +#: ../../../../inc/config.php:1045 ../../../../inc/config.php:1048 +msgid "You didn't select anything to report." +msgstr "Ви не вибрали жодного допису, щоб поскаржитися." + +#: ../../../../inc/config.php:892 ../../../../inc/config.php:997 +#: ../../../../inc/config.php:988 ../../../../inc/config.php:990 +#: ../../../../inc/config.php:992 ../../../../inc/config.php:1008 +#: ../../../../inc/config.php:1017 ../../../../inc/config.php:1043 +#: ../../../../inc/config.php:1046 ../../../../inc/config.php:1049 +msgid "You can't report that many posts at once." +msgstr "Ви не можете поскаржитися на таку кількість дописів відразу." + +#: ../../../../inc/config.php:893 ../../../../inc/config.php:998 +#: ../../../../inc/config.php:989 ../../../../inc/config.php:991 +#: ../../../../inc/config.php:993 ../../../../inc/config.php:1009 +#: ../../../../inc/config.php:1018 ../../../../inc/config.php:1044 +#: ../../../../inc/config.php:1047 ../../../../inc/config.php:1050 +msgid "Wrong password…" +msgstr "Неправильний пароль…" + +#: ../../../../inc/config.php:894 ../../../../inc/config.php:999 +#: ../../../../inc/config.php:990 ../../../../inc/config.php:992 +#: ../../../../inc/config.php:994 ../../../../inc/config.php:1010 +#: ../../../../inc/config.php:1019 ../../../../inc/config.php:1045 +#: ../../../../inc/config.php:1048 ../../../../inc/config.php:1051 +msgid "Invalid image." +msgstr "Неправильне зображення." + +#: ../../../../inc/config.php:895 ../../../../inc/config.php:1000 +#: ../../../../inc/config.php:991 ../../../../inc/config.php:993 +#: ../../../../inc/config.php:995 ../../../../inc/config.php:1011 +#: ../../../../inc/config.php:1020 ../../../../inc/config.php:1046 +#: ../../../../inc/config.php:1049 ../../../../inc/config.php:1052 +msgid "Unknown file extension." +msgstr "Невідоме розширення файла." + +#: ../../../../inc/config.php:896 ../../../../inc/config.php:1001 +#: ../../../../inc/config.php:992 ../../../../inc/config.php:994 +#: ../../../../inc/config.php:996 ../../../../inc/config.php:1012 +#: ../../../../inc/config.php:1021 ../../../../inc/config.php:1047 +#: ../../../../inc/config.php:1050 ../../../../inc/config.php:1053 +msgid "Maximum file size: %maxsz% bytes
    Your file's size: %filesz% bytes" +msgstr "" +"Максимальний розмір файла: %maxsz% байтів
    Розмір вашого файла: %filesz% " +"байтів" + +#: ../../../../inc/config.php:897 ../../../../inc/config.php:1002 +#: ../../../../inc/config.php:993 ../../../../inc/config.php:995 +#: ../../../../inc/config.php:997 ../../../../inc/config.php:1013 +#: ../../../../inc/config.php:1022 ../../../../inc/config.php:1048 +#: ../../../../inc/config.php:1051 ../../../../inc/config.php:1054 +msgid "The file was too big." +msgstr "Файл завеликий." + +#: ../../../../inc/config.php:898 ../../../../inc/config.php:1003 +#: ../../../../inc/config.php:994 ../../../../inc/config.php:996 +#: ../../../../inc/config.php:998 ../../../../inc/config.php:1014 +#: ../../../../inc/config.php:1023 ../../../../inc/config.php:1053 +#: ../../../../inc/config.php:1057 ../../../../inc/config.php:1060 +#, php-format +msgid "That file already exists!" +msgstr "Файл уже існує!" + +#: ../../../../inc/config.php:899 ../../../../inc/config.php:1004 +#: ../../../../inc/config.php:995 ../../../../inc/config.php:997 +#: ../../../../inc/config.php:999 ../../../../inc/config.php:1015 +#: ../../../../inc/config.php:1024 ../../../../inc/config.php:1054 +#: ../../../../inc/config.php:1058 ../../../../inc/config.php:1061 +#, php-format +msgid "That file already exists in this thread!" +msgstr "Файл уже існує у цій нитці!" + +#: ../../../../inc/config.php:900 ../../../../inc/config.php:1005 +#: ../../../../inc/config.php:996 ../../../../inc/config.php:998 +#: ../../../../inc/config.php:1000 ../../../../inc/config.php:1016 +#: ../../../../inc/config.php:1025 ../../../../inc/config.php:1055 +#: ../../../../inc/config.php:1059 ../../../../inc/config.php:1062 +#, php-format +msgid "You'll have to wait another %s before deleting that." +msgstr "Почекайте ще %s перед видаленням." + +#: ../../../../inc/config.php:901 ../../../../inc/config.php:1006 +#: ../../../../inc/config.php:997 ../../../../inc/config.php:999 +#: ../../../../inc/config.php:1001 ../../../../inc/config.php:1017 +#: ../../../../inc/config.php:1026 ../../../../inc/config.php:1056 +#: ../../../../inc/config.php:1060 ../../../../inc/config.php:1063 +msgid "MIME type detection XSS exploit (IE) detected; post discarded." +msgstr "Виявлено XSS-експлойт визначення MIME-типу — допис відхилено." + +#: ../../../../inc/config.php:902 ../../../../inc/config.php:1007 +#: ../../../../inc/config.php:998 ../../../../inc/config.php:1000 +#: ../../../../inc/config.php:1002 ../../../../inc/config.php:1018 +#: ../../../../inc/config.php:1027 ../../../../inc/config.php:1057 +#: ../../../../inc/config.php:1061 ../../../../inc/config.php:1064 +msgid "Couldn't make sense of the URL of the video you tried to embed." +msgstr "Не вдалося розпізнати адресу відео, яке ви намагалися вбудувати." + +#: ../../../../inc/config.php:903 ../../../../inc/config.php:1008 +#: ../../../../inc/config.php:999 ../../../../inc/config.php:1001 +#: ../../../../inc/config.php:1003 ../../../../inc/config.php:1019 +#: ../../../../inc/config.php:1028 ../../../../inc/config.php:1058 +#: ../../../../inc/config.php:1062 ../../../../inc/config.php:1065 +msgid "You seem to have mistyped the verification." +msgstr "Здається, ви не пройшли підтвердження." + +#. Moderator errors +#: ../../../../inc/config.php:906 ../../../../inc/config.php:1011 +#: ../../../../inc/config.php:1002 ../../../../inc/config.php:1004 +#: ../../../../inc/config.php:1006 ../../../../inc/config.php:1022 +#: ../../../../inc/config.php:1031 ../../../../inc/config.php:1062 +#: ../../../../inc/config.php:1066 ../../../../inc/config.php:1069 +#, php-format +msgid "" +"You are only allowed to unban %s users at a time. You tried to unban %u " +"users." +msgstr "" +"Дозволяється розблоковувати лише %s користувачів за раз. Ви намагались " +"розблокувати %u користувачів." + +#: ../../../../inc/config.php:907 ../../../../inc/config.php:1012 +#: ../../../../inc/config.php:1003 ../../../../inc/config.php:1005 +#: ../../../../inc/config.php:1007 ../../../../inc/config.php:1023 +#: ../../../../inc/config.php:1032 ../../../../inc/config.php:1063 +#: ../../../../inc/config.php:1067 ../../../../inc/config.php:1070 +msgid "Invalid username and/or password." +msgstr "Неправильне імʼя користувача і/або пароль." + +#: ../../../../inc/config.php:908 ../../../../inc/config.php:1013 +#: ../../../../inc/config.php:1004 ../../../../inc/config.php:1006 +#: ../../../../inc/config.php:1008 ../../../../inc/config.php:1024 +#: ../../../../inc/config.php:1033 ../../../../inc/config.php:1064 +#: ../../../../inc/config.php:1068 ../../../../inc/config.php:1071 +msgid "You are not a mod…" +msgstr "Ви не модератор…" + +#: ../../../../inc/config.php:909 ../../../../inc/config.php:1014 +#: ../../../../inc/config.php:1005 ../../../../inc/config.php:1007 +#: ../../../../inc/config.php:1009 ../../../../inc/config.php:1025 +#: ../../../../inc/config.php:1034 ../../../../inc/config.php:1065 +#: ../../../../inc/config.php:1069 ../../../../inc/config.php:1072 +msgid "" +"Invalid username and/or password. Your user may have been deleted or changed." +msgstr "" +"Неправильне імʼя користувача і/або пароль. Можливо, ваш обліковий запис " +"видалено або змінено." + +#: ../../../../inc/config.php:910 ../../../../inc/config.php:1015 +#: ../../../../inc/config.php:1006 ../../../../inc/config.php:1008 +#: ../../../../inc/config.php:1010 ../../../../inc/config.php:1026 +#: ../../../../inc/config.php:1035 ../../../../inc/config.php:1066 +#: ../../../../inc/config.php:1070 ../../../../inc/config.php:1073 +msgid "Invalid/malformed cookies." +msgstr "Неправильні/неправильно сформовані куки." + +#: ../../../../inc/config.php:911 ../../../../inc/config.php:1016 +#: ../../../../inc/config.php:1007 ../../../../inc/config.php:1009 +#: ../../../../inc/config.php:1011 ../../../../inc/config.php:1027 +#: ../../../../inc/config.php:1036 ../../../../inc/config.php:1067 +#: ../../../../inc/config.php:1071 ../../../../inc/config.php:1074 +msgid "Your browser didn't submit an input when it should have." +msgstr "Ваш браузер не підтвердив введення тоді, коли потрібно." + +#: ../../../../inc/config.php:912 ../../../../inc/config.php:1017 +#: ../../../../inc/config.php:1008 ../../../../inc/config.php:1010 +#: ../../../../inc/config.php:1012 ../../../../inc/config.php:1028 +#: ../../../../inc/config.php:1037 ../../../../inc/config.php:1068 +#: ../../../../inc/config.php:1072 ../../../../inc/config.php:1075 +#, php-format +msgid "The %s field is required." +msgstr "Поле %s обовʼязкове." + +#: ../../../../inc/config.php:913 ../../../../inc/config.php:1018 +#: ../../../../inc/config.php:1009 ../../../../inc/config.php:1011 +#: ../../../../inc/config.php:1013 ../../../../inc/config.php:1029 +#: ../../../../inc/config.php:1038 ../../../../inc/config.php:1069 +#: ../../../../inc/config.php:1073 ../../../../inc/config.php:1076 +#, php-format +msgid "The %s field was invalid." +msgstr "Поле %s заповнено неправильно." + +#: ../../../../inc/config.php:914 ../../../../inc/config.php:1019 +#: ../../../../inc/config.php:1010 ../../../../inc/config.php:1012 +#: ../../../../inc/config.php:1014 ../../../../inc/config.php:1030 +#: ../../../../inc/config.php:1039 ../../../../inc/config.php:1070 +#: ../../../../inc/config.php:1074 ../../../../inc/config.php:1077 +#, php-format +msgid "There is already a %s board." +msgstr "Дошка %s уже існує." + +#: ../../../../inc/config.php:915 ../../../../inc/config.php:1020 +#: ../../../../inc/config.php:1011 ../../../../inc/config.php:1013 +#: ../../../../inc/config.php:1015 ../../../../inc/config.php:1031 +#: ../../../../inc/config.php:1040 ../../../../inc/config.php:1071 +#: ../../../../inc/config.php:1075 ../../../../inc/config.php:1078 +msgid "You don't have permission to do that." +msgstr "У вас відсутній дозвіл на цю дію." + +#: ../../../../inc/config.php:916 ../../../../inc/config.php:1021 +#: ../../../../inc/config.php:1012 ../../../../inc/config.php:1014 +#: ../../../../inc/config.php:1016 ../../../../inc/config.php:1032 +#: ../../../../inc/config.php:1041 ../../../../inc/config.php:1072 +#: ../../../../inc/config.php:1076 ../../../../inc/config.php:1079 +msgid "That post doesn't exist…" +msgstr "Цей допис не існує…" + +#: ../../../../inc/config.php:917 ../../../../inc/config.php:1022 +#: ../../../../inc/config.php:1013 ../../../../inc/config.php:1015 +#: ../../../../inc/config.php:1017 ../../../../inc/config.php:1033 +#: ../../../../inc/config.php:1042 ../../../../inc/config.php:1073 +#: ../../../../inc/config.php:1077 ../../../../inc/config.php:1080 +msgid "Page not found." +msgstr "Сторінку не знайдено." + +#: ../../../../inc/config.php:918 ../../../../inc/config.php:1023 +#: ../../../../inc/config.php:1014 ../../../../inc/config.php:1016 +#: ../../../../inc/config.php:1018 ../../../../inc/config.php:1034 +#: ../../../../inc/config.php:1043 ../../../../inc/config.php:1074 +#: ../../../../inc/config.php:1078 ../../../../inc/config.php:1081 +#, php-format +msgid "That mod already exists!" +msgstr "Модератор уже існує!" + +#: ../../../../inc/config.php:919 ../../../../inc/config.php:1024 +#: ../../../../inc/config.php:1015 ../../../../inc/config.php:1017 +#: ../../../../inc/config.php:1019 ../../../../inc/config.php:1035 +#: ../../../../inc/config.php:1044 ../../../../inc/config.php:1075 +#: ../../../../inc/config.php:1079 ../../../../inc/config.php:1082 +msgid "That theme doesn't exist!" +msgstr "Цієї теми не існує!" + +# Може, ключ? +#: ../../../../inc/config.php:920 ../../../../inc/config.php:1025 +#: ../../../../inc/config.php:1016 ../../../../inc/config.php:1018 +#: ../../../../inc/config.php:1020 ../../../../inc/config.php:1036 +#: ../../../../inc/config.php:1045 ../../../../inc/config.php:1076 +#: ../../../../inc/config.php:1080 ../../../../inc/config.php:1083 +msgid "Invalid security token! Please go back and try again." +msgstr "Неправильний токен безпеки! Поверніться і спробуйте ще раз." + +#. Default public ban message. In public ban messages, %length% is replaced +#. with "for x days" or +#. "permanently" (with %LENGTH% being the uppercase equivalent). +#. Default public ban message. In public ban messages, %length% is replaced with "for x days" or +#. "permanently" (with %LENGTH% being the uppercase equivalent). +#: ../../../../inc/config.php:1086 ../../../../inc/config.php:1189 +#: ../../../../inc/config.php:1180 ../../../../inc/config.php:1185 +#: ../../../../inc/config.php:1187 ../../../../inc/config.php:1203 +#: ../../../../inc/config.php:1212 ../../../../inc/config.php:1243 +#: ../../../../inc/config.php:1247 ../../../../inc/config.php:1250 +msgid "USER WAS BANNED FOR THIS POST" +msgstr "АВТОРА ЦЬОГО ДОПИСУ ЗАБЛОКОВАНО" + +#: ../../../../inc/mod/pages.php:66 ../../../../inc/mod/pages.php:64 +msgid "Confirm action" +msgstr "Підтвердити дію" + +#: ../../../../inc/mod/pages.php:110 ../../../../inc/mod/pages.php:108 +msgid "Could not find current version! (Check .installed)" +msgstr "Не вдалося знайти поточної версії! (перевірте .installed)" + +#: ../../../../inc/mod/pages.php:162 +msgid "Dashboard" +msgstr "Панель керування" + +#: ../../../../inc/mod/pages.php:267 ../../../../inc/mod/pages.php:265 +msgid "There are no boards to search!" +msgstr "Немає дошок для пошуку!" + +#. $results now contains the search results +#: ../../../../inc/mod/pages.php:335 ../../../../inc/mod/pages.php:334 +msgid "Search results" +msgstr "Результати пошуку" + +#: ../../../../inc/mod/pages.php:436 ../../../../inc/mod/pages.php:438 +msgid "Edit board" +msgstr "Редагувати дошку" + +#: ../../../../inc/mod/pages.php:486 ../../../../inc/mod/pages.php:491 +msgid "Couldn't open board after creation." +msgstr "Не вдалося відкрити дошку після створення." + +#: ../../../../inc/mod/pages.php:506 ../../../../inc/mod/pages.php:511 +msgid "New board" +msgstr "Нова дошка" + +#. line 37 +#: ../../../../inc/mod/pages.php:553 ../../../../inc/mod/pages.php:562 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:121 +msgid "Noticeboard" +msgstr "Дошка оголошень" + +#: ../../../../inc/mod/pages.php:614 ../../../../inc/mod/pages.php:631 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:194 +msgid "News" +msgstr "Новини" + +#: ../../../../inc/mod/pages.php:654 ../../../../inc/mod/pages.php:681 +#: ../../../../inc/mod/pages.php:671 ../../../../inc/mod/pages.php:698 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:300 +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:75 +msgid "Moderation log" +msgstr "Журнал модерації" + +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#. line 104 +#. line 20 +#. line 18 +#: ../../../../inc/mod/pages.php:838 ../../../../inc/mod/pages.php:852 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:275 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:71 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:64 +#: ../../../../inc/mod/pages.php:849 +msgid "IP" +msgstr "IP" + +#. line 171 +#: ../../../../inc/mod/pages.php:848 ../../../../inc/mod/pages.php:1367 +#: ../../../../inc/mod/pages.php:862 ../../../../inc/mod/pages.php:1432 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:440 +#: ../../../../inc/mod/pages.php:1446 ../../../../inc/mod/pages.php:1447 +#: ../../../../inc/mod/pages.php:859 ../../../../inc/mod/pages.php:1444 +#: ../../../../inc/mod/pages.php:1449 +msgid "New ban" +msgstr "Нове блокування" + +#: ../../../../inc/mod/pages.php:931 ../../../../inc/mod/pages.php:914 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:256 +#: ../../../../inc/mod/pages.php:911 ../../../../inc/mod/pages.php:903 +#: ../../../../templates/themes/public_banlist/theme.php:42 +msgid "Ban list" +msgstr "Список блокувань" + +#. line 38 +#: ../../../../inc/mod/pages.php:1105 ../../../../inc/mod/pages.php:1165 +#: ../../../../inc/mod/pages.php:1172 +#: ../../../../templates/cache/56/25/ac2c51fa6b3e26f9f9ed7dda5224acfbec96881d648c8ded10c5eef2c3e5.php:108 +#: ../../../../inc/mod/pages.php:1173 ../../../../inc/mod/pages.php:1170 +#: ../../../../inc/mod/pages.php:1175 +msgid "Move reply" +msgstr "Перемістити відповідь" + +#: ../../../../inc/mod/pages.php:1131 ../../../../inc/mod/pages.php:1191 +#: ../../../../inc/mod/pages.php:1198 ../../../../inc/mod/pages.php:1199 +#: ../../../../inc/mod/pages.php:1196 ../../../../inc/mod/pages.php:1201 +msgid "Target and source board are the same." +msgstr "Початкова дошка і дошка призначення збігаються." + +#: ../../../../inc/mod/pages.php:1296 ../../../../inc/mod/pages.php:1357 +#: ../../../../inc/mod/pages.php:1371 ../../../../inc/mod/pages.php:1372 +#: ../../../../inc/mod/pages.php:1369 ../../../../inc/mod/pages.php:1374 +msgid "Impossible to move thread; there is only one board." +msgstr "Неможливо перемістити нитку: дошка тільки одна." + +#. line 39 +#: ../../../../inc/mod/pages.php:1300 ../../../../inc/mod/pages.php:1361 +#: ../../../../templates/cache/32/3a/d7e02cef5846ec4f1f423bb0ad2d3c307845d29f70da3f8a90a41f873e7d.php:114 +#: ../../../../inc/mod/pages.php:1375 ../../../../inc/mod/pages.php:1376 +#: ../../../../inc/mod/pages.php:1373 ../../../../inc/mod/pages.php:1378 +msgid "Move thread" +msgstr "Перемістити нитку" + +#: ../../../../inc/mod/pages.php:1698 ../../../../inc/mod/pages.php:1751 +#: ../../../../inc/mod/pages.php:1775 ../../../../inc/mod/pages.php:1791 +#: ../../../../inc/mod/pages.php:1792 ../../../../inc/mod/pages.php:1796 +msgid "Edit user" +msgstr "Редагувати користувача" + +#: ../../../../inc/mod/pages.php:1764 ../../../../inc/mod/pages.php:1855 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:274 +#: ../../../../inc/mod/pages.php:1871 ../../../../inc/mod/pages.php:1872 +#: ../../../../inc/mod/pages.php:1876 +msgid "Manage users" +msgstr "Керування користувачами" + +#. deleted? +#: ../../../../inc/mod/pages.php:1826 ../../../../inc/mod/pages.php:1899 +#: ../../../../inc/mod/pages.php:1945 ../../../../inc/mod/pages.php:2021 +#: ../../../../inc/mod/pages.php:1961 ../../../../inc/mod/pages.php:2037 +#: ../../../../inc/mod/pages.php:1962 ../../../../inc/mod/pages.php:2038 +#: ../../../../inc/mod/pages.php:1966 ../../../../inc/mod/pages.php:2042 +msgid "New PM for" +msgstr "Нове приватне повідомлення до" + +#: ../../../../inc/mod/pages.php:1830 ../../../../inc/mod/pages.php:1952 +#: ../../../../inc/mod/pages.php:1968 ../../../../inc/mod/pages.php:1969 +#: ../../../../inc/mod/pages.php:1973 +msgid "Private message" +msgstr "Приватне повідомлення" + +#. line 68 +#: ../../../../inc/mod/pages.php:1851 ../../../../inc/mod/pages.php:1973 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:200 +#: ../../../../inc/mod/pages.php:1989 ../../../../inc/mod/pages.php:1990 +#: ../../../../inc/mod/pages.php:1994 +msgid "PM inbox" +msgstr "Скринька вхідних приватних повідомлень" + +#: ../../../../inc/mod/pages.php:1963 ../../../../inc/mod/pages.php:1967 +#: ../../../../inc/mod/pages.php:2090 ../../../../inc/mod/pages.php:2094 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:309 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:115 +#: ../../../../inc/mod/pages.php:2106 ../../../../inc/mod/pages.php:2110 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:318 +#: ../../../../inc/mod/pages.php:2107 ../../../../inc/mod/pages.php:2111 +#: ../../../../inc/mod/pages.php:2115 +msgid "Rebuild" +msgstr "Перебудувати" + +#: ../../../../inc/mod/pages.php:2043 ../../../../inc/mod/pages.php:2179 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:238 +#: ../../../../inc/mod/pages.php:2195 ../../../../inc/mod/pages.php:2196 +#: ../../../../inc/mod/pages.php:2200 +msgid "Report queue" +msgstr "Черга скарг" + +#: ../../../../inc/mod/pages.php:2111 ../../../../inc/mod/pages.php:2210 +#: ../../../../inc/mod/pages.php:2256 ../../../../inc/mod/pages.php:2350 +#: ../../../../inc/mod/pages.php:2334 ../../../../inc/mod/pages.php:2428 +#: ../../../../inc/mod/pages.php:2328 ../../../../inc/mod/pages.php:2422 +#: ../../../../inc/mod/pages.php:2327 ../../../../inc/mod/pages.php:2421 +#: ../../../../inc/mod/pages.php:2332 ../../../../inc/mod/pages.php:2426 +msgid "Config editor" +msgstr "Редактор конфігурації" + +#: ../../../../inc/mod/pages.php:2226 ../../../../inc/mod/pages.php:2367 +#: ../../../../inc/mod/pages.php:2445 ../../../../inc/mod/pages.php:2439 +#: ../../../../inc/mod/pages.php:2438 ../../../../inc/mod/pages.php:2443 +msgid "Themes directory doesn't exist!" +msgstr "Каталог з темами не існує!" + +#: ../../../../inc/mod/pages.php:2228 ../../../../inc/mod/pages.php:2369 +#: ../../../../inc/mod/pages.php:2447 ../../../../inc/mod/pages.php:2441 +#: ../../../../inc/mod/pages.php:2440 ../../../../inc/mod/pages.php:2445 +msgid "Cannot open themes directory; check permissions." +msgstr "Не вдалося відкрити каталог з темами, перевірте дозволи." + +#: ../../../../inc/mod/pages.php:2242 ../../../../inc/mod/pages.php:2388 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:291 +#: ../../../../inc/mod/pages.php:2466 ../../../../inc/mod/pages.php:2460 +#: ../../../../inc/mod/pages.php:2459 ../../../../inc/mod/pages.php:2464 +msgid "Manage themes" +msgstr "Керування темами" + +#: ../../../../inc/mod/pages.php:2307 ../../../../inc/mod/pages.php:2453 +#: ../../../../inc/mod/pages.php:2531 ../../../../inc/mod/pages.php:2525 +#: ../../../../inc/mod/pages.php:2524 ../../../../inc/mod/pages.php:2529 +#, php-format +msgid "Installed theme: %s" +msgstr "Встановлена теми: %s" + +#: ../../../../inc/mod/pages.php:2318 ../../../../inc/mod/pages.php:2464 +#: ../../../../inc/mod/pages.php:2542 ../../../../inc/mod/pages.php:2536 +#: ../../../../inc/mod/pages.php:2535 ../../../../inc/mod/pages.php:2540 +#, php-format +msgid "Configuring theme: %s" +msgstr "Конфігурується тема: %s" + +#: ../../../../inc/mod/pages.php:2346 ../../../../inc/mod/pages.php:2493 +#: ../../../../inc/mod/pages.php:2571 ../../../../inc/mod/pages.php:2565 +#: ../../../../inc/mod/pages.php:2564 ../../../../inc/mod/pages.php:2569 +#, php-format +msgid "Rebuilt theme: %s" +msgstr "Перебудовано тему: %s" + +#: ../../../../inc/mod/pages.php:2385 ../../../../inc/mod/pages.php:2532 +#: ../../../../inc/mod/pages.php:2610 ../../../../inc/mod/pages.php:2604 +#: ../../../../inc/mod/pages.php:2603 ../../../../inc/mod/pages.php:2608 +msgid "Debug: Anti-spam" +msgstr "Зневадження: Антиспам" + +#: ../../../../inc/mod/pages.php:2409 ../../../../inc/mod/pages.php:2566 +#: ../../../../inc/mod/pages.php:2644 ../../../../inc/mod/pages.php:2638 +#: ../../../../inc/mod/pages.php:2637 ../../../../inc/mod/pages.php:2642 +msgid "Debug: Recent posts" +msgstr "Зневадження: Останні пости" + +#: ../../../../inc/mod/pages.php:2433 ../../../../inc/mod/pages.php:2590 +#: ../../../../inc/mod/pages.php:2668 ../../../../inc/mod/pages.php:2662 +#: ../../../../inc/mod/pages.php:2661 ../../../../inc/mod/pages.php:2666 +msgid "Debug: SQL" +msgstr "Зневадження: SQL" + +#. Print error +#: ../../../../inc/database.php:72 ../../../../inc/database.php:94 +msgid "Database error: " +msgstr "Помилка бази даних:" + +#: ../../../../banned.php:4 +msgid "Banned?" +msgstr "Заблоковано?" + +#: ../../../../banned.php:5 +msgid "You are not banned." +msgstr "Вас не заблоковано." + +#. line 6 +#: ../../../../templates/cache/3c/80/0ebbee302f4fad8d0d7f13e62db5.php:41 +#: ../../../../templates/cache/e1/4c/f58701138b0d44bc13ada3e46deec60da83d42ff4f39720ccd6955b641f7.php:44 +msgid "Go back" +msgstr "Повернутися" + +#. line 13 +#: ../../../../templates/cache/3c/80/0ebbee302f4fad8d0d7f13e62db5.php:56 +#: ../../../../templates/cache/e1/4c/f58701138b0d44bc13ada3e46deec60da83d42ff4f39720ccd6955b641f7.php:59 +msgid "Error information" +msgstr "Інформація про помилку" + +#. line 2 +#. line 3 +#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:22 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:25 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:27 +msgid "Delete Post" +msgstr "Видалити допис" + +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 84 +#. line 3 +#. line 97 +#. line 3 +#. line 97 +#. line 3 +#. line 97 +#. line 4 +#. line 97 +#. line 4 +#. line 97 +#. line 4 +#. line 97 +#. line 4 +#. line 97 +#. line 4 +#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:26 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:250 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:253 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:29 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:290 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:31 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:301 +msgid "File" +msgstr "Файл" + +#. line 132 +#. line 14 +#. line 132 +#. line 14 +#. line 131 +#. line 14 +#. line 131 +#. line 14 +#. line 131 +#. line 14 +#. line 131 +#. line 21 +#. line 14 +#. line 131 +#. line 14 +#. line 144 +#. line 14 +#. line 144 +#. line 21 +#. line 14 +#. line 144 +#. line 21 +#. line 14 +#. line 144 +#. line 21 +#. line 14 +#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:28 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:364 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:367 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:31 +#: ../../../../templates/cache/00/31/a027d7b6d57819b6e43e58620f3f4c76194dd75db65fc888a5053ce62803.php:48 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:363 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:69 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:400 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:33 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:411 +msgid "Password" +msgstr "Пароль" + +#. line 8 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 8 +#. line 108 +#. line 32 +#. line 8 +#. line 32 +#. line 23 +#. line 8 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 8 +#. line 5 +#. line 8 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 5 +#. line 8 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 48 +#. line 5 +#. line 8 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 48 +#. line 5 +#. line 8 +#. line 108 +#. line 32 +#. line 5 +#. line 8 +#. line 108 +#. line 32 +#. line 23 +#. line 5 +#. line 8 +#. line 108 +#. line 32 +#. line 23 +#. line 5 +#. line 10 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 48 +#. line 5 +#. line 10 +#. line 108 +#. line 32 +#. line 9 +#. line 23 +#. line 48 +#. line 10 +#. line 5 +#. line 10 +#. line 108 +#. line 32 +#. line 23 +#. line 48 +#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:39 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:42 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:285 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:99 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:43 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:77 +#: ../../../../templates/cache/3a/62/f804928dbcf285e3d5d8d7ae31b1e3a7c78264f270efa9650d31f69c7897.php:37 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:141 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:47 +msgid "Reason" +msgstr "Причина" + +#. line 10 +#. line 12 +#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:44 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:47 +#: ../../../../templates/cache/17/2f/ea79f6d94768f645ed33b3f5c1a54caee235af04d24b88e34cc8c2d48583.php:52 +msgid "Report" +msgstr "Поскаржитися" + +#: ../../../../templates/cache/f5/e3/343716327c6183713f70a3fb57f1.php:149 +#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:134 +#: ../../../../templates/cache/62/8c/21348d46377c3e1b3f8c476ba376.php:65 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:137 +#: ../../../../templates/cache/26/b3/eb11457d26f281883c21fad69f55f2c85f7cde1e4986db8692a12aaf72a5.php:153 +#: ../../../../templates/cache/e5/67/00152f100a684a6ff580e1afded8e907bdea04b4818d725ebfeb103d70d9.php:68 +#: ../../../../templates/cache/e5/67/00152f100a684a6ff580e1afded8e907bdea04b4818d725ebfeb103d70d9.php:71 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:120 +#: ../../../../templates/cache/26/b3/eb11457d26f281883c21fad69f55f2c85f7cde1e4986db8692a12aaf72a5.php:138 +#: ../../../../templates/cache/26/b3/eb11457d26f281883c21fad69f55f2c85f7cde1e4986db8692a12aaf72a5.php:129 +msgid "Return to dashboard" +msgstr "Повернутися до панелі керування" + +#. line 39 +#. line 33 +#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:143 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:146 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:134 +msgid "Posting mode: Reply" +msgstr "Режим дописування: відповідь" + +#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:147 +#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:200 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:150 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:203 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:138 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:186 +msgid "Return" +msgstr "Повернутися" + +#: ../../../../templates/cache/f3/ad/68dee281a64ebad9a5c774b53279.php:61 +#: ../../../../templates/cache/d2/14/70c07e4c5f648cfa0d0663a1f18973ff6f6946363b45332b2627a0fcf273.php:64 +msgid "(No news to show.)" +msgstr "(немає новин)" + +#: ../../../../templates/cache/f3/ad/68dee281a64ebad9a5c774b53279.php:85 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:146 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:116 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:144 +#: ../../../../templates/cache/d2/14/70c07e4c5f648cfa0d0663a1f18973ff6f6946363b45332b2627a0fcf273.php:88 +msgid "no subject" +msgstr "без заголовка" + +#. line 44 +#. line 56 +#. line 44 +#. line 56 +#. line 44 +#: ../../../../templates/cache/f3/ad/68dee281a64ebad9a5c774b53279.php:91 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:125 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:153 +#: ../../../../templates/cache/d2/14/70c07e4c5f648cfa0d0663a1f18973ff6f6946363b45332b2627a0fcf273.php:94 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:124 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:132 +msgid "by" +msgstr "модератором/адміністратором" + +#. line 50 +#: ../../../../templates/cache/f3/ad/68dee281a64ebad9a5c774b53279.php:95 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:146 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:157 +#: ../../../../templates/cache/d2/14/70c07e4c5f648cfa0d0663a1f18973ff6f6946363b45332b2627a0fcf273.php:98 +msgid "at" +msgstr "о" + +#. line 28 +#. line 26 +#: ../../../../templates/cache/4b/3e/915cc5ac5fe144c331207c656528.php:99 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:102 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:88 +msgid "1 reply" +msgid_plural "%count% replies" +msgstr[0] "%count% відповідь" +msgstr[1] "%count% відповіді" +msgstr[2] "%count% відповідей" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:102 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:105 +msgid "File:" +msgstr "Файл:" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:115 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:127 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:165 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:206 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:118 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:130 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:168 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:209 +#: ../../../../templates/cache/9b/6a/0d0c5add399e8dfbd5c8c097ea68815306312248498dae4682282190e561.php:128 +#: ../../../../templates/cache/f4/a7/ad2833eb0c0460ae8ae508f0d0846fd7a06aafcf8ef126ae76721e92f42a.php:82 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:141 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:179 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:220 +msgid "Spoiler Image" +msgstr "Спойлерне зображення" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:530 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:495 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:506 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:532 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:591 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:304 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:216 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:227 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:229 +msgid "Reply" +msgstr "Відповісти" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:544 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:509 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:520 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:546 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:605 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:318 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:230 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:241 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:243 +msgid "View All" +msgstr "Показати все" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:561 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:526 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:537 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:563 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:622 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:335 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:247 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:258 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:260 +msgid "Last 1 Post" +msgid_plural "Last %count% Posts" +msgstr[0] "Останній %count% допис" +msgstr[1] "Останні %count% дописи" +msgstr[2] "Останні %count% дописів" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:598 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:563 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:574 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:600 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:659 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:372 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:284 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:295 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:297 +msgid "1 post" +msgid_plural "%count% posts" +msgstr[0] "%count% допис" +msgstr[1] "%count% дописи" +msgstr[2] "%count% дописів" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:604 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:569 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:580 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:116 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:606 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:665 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:378 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:290 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:301 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:303 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:141 +msgid "and" +msgstr "і" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:616 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:581 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:592 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:618 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:677 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:390 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:302 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:313 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:315 +msgid "1 image reply" +msgid_plural "%count% image replies" +msgstr[0] "%count% допис із зображеннями" +msgstr[1] "%count% дописи із зображеннями" +msgstr[2] "%count% дописів із зображеннями" + +#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:621 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:586 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:597 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:623 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:682 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:395 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:307 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:318 +#: ../../../../templates/cache/b5/eb/fd7d06d38210e123d492fb7f2a1891578af746ef421003f1b55da157122f.php:320 +msgid "omitted. Click reply to view." +msgstr "пропущено. Натисніть «Відповісти», щоб побачити." + +#. line 7 +#. line 14 +#. line 7 +#. line 14 +#. line 8 +#. line 14 +#. line 7 +#. line 8 +#. line 14 +#. line 8 +#. line 14 +#. line 8 +#. line 14 +#. line 7 +#. line 8 +#. line 7 +#. line 14 +#. line 8 +#. line 14 +#. line 7 +#. line 8 +#. line 14 +#. line 7 +#. line 8 +#. line 14 +#. line 7 +#. line 8 +#. line 14 +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:30 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:66 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:38 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:42 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:48 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:69 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:33 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:40 +msgid "Name" +msgstr "Імʼя" + +#. line 15 +#. line 24 +#. line 15 +#. line 24 +#. line 15 +#. line 24 +#. line 15 +#. line 24 +#. line 15 +#. line 24 +#. line 15 +#. line 24 +#. line 15 +#. line 24 +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:44 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:88 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:91 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:47 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:97 +msgid "Email" +msgstr "Ел. пошта" + +#. line 23 +#. line 46 +#. line 23 +#. line 46 +#. line 12 +#. line 24 +#. line 46 +#. line 23 +#. line 12 +#. line 46 +#. line 12 +#. line 46 +#. line 12 +#. line 46 +#. line 23 +#. line 12 +#. line 24 +#. line 46 +#. line 12 +#. line 46 +#. line 23 +#. line 12 +#. line 46 +#. line 23 +#. line 12 +#. line 46 +#. line 23 +#. line 12 +#. line 46 +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:58 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:147 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:48 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:76 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:150 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:61 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:161 +msgid "Subject" +msgstr "Тема" + +#. line 27 +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:68 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:71 +msgid "Update" +msgstr "Оновлення" + +#. line 32 +#. line 57 +#. line 32 +#. line 57 +#. line 32 +#. line 57 +#. line 32 +#. line 57 +#. line 32 +#. line 57 +#. line 32 +#. line 57 +#. line 32 +#. line 57 +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:76 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:178 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:181 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:79 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:192 +msgid "Comment" +msgstr "Допис" + +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:97 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:100 +msgid "Currently editing raw HTML." +msgstr "Зараз редагується сирий HTML" + +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:105 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:108 +msgid "Edit markup instead?" +msgstr "Редагувати натомість розмітку?" + +#: ../../../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:115 +#: ../../../../templates/cache/d1/2d/e4ea563232b42da227befa9cf03fef935e472b1268221e2399d8d6af1cd5.php:118 +msgid "Edit raw HTML instead?" +msgstr "Редагувати натомість сирий HTML?" + +#. line 73 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:226 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:229 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:240 +msgid "Verification" +msgstr "Підтвердження" + +#. line 90 +#. line 103 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:262 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:265 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:302 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:313 +msgid "Or URL" +msgstr "Або URL" + +#. line 100 +#. line 113 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:282 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:285 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:322 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:333 +msgid "Embed" +msgstr "Вбудувати" + +#. line 112 +#. line 111 +#. line 124 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:306 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:309 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:305 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:342 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:353 +msgid "Flags" +msgstr "Позначки" + +#. line 116 +#. line 117 +#. line 116 +#. line 117 +#. line 116 +#. line 117 +#. line 116 +#. line 117 +#. line 115 +#. line 116 +#. line 115 +#. line 116 +#. line 115 +#. line 116 +#. line 115 +#. line 116 +#. line 115 +#. line 116 +#. line 115 +#. line 116 +#. line 115 +#. line 116 +#. line 128 +#. line 129 +#. line 128 +#. line 129 +#. line 128 +#. line 129 +#. line 128 +#. line 129 +#. line 128 +#. line 129 +#. line 128 +#. line 129 +#. line 128 +#. line 129 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:316 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:320 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:319 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:323 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:315 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:352 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:356 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:363 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:367 +msgid "Sticky" +msgstr "Прикріплений" + +#. line 120 +#. line 121 +#. line 120 +#. line 121 +#. line 120 +#. line 121 +#. line 120 +#. line 121 +#. line 119 +#. line 120 +#. line 119 +#. line 120 +#. line 119 +#. line 120 +#. line 119 +#. line 120 +#. line 119 +#. line 120 +#. line 119 +#. line 120 +#. line 119 +#. line 120 +#. line 132 +#. line 133 +#. line 132 +#. line 133 +#. line 132 +#. line 133 +#. line 132 +#. line 133 +#. line 132 +#. line 133 +#. line 132 +#. line 133 +#. line 132 +#. line 133 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:330 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:334 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:333 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:337 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:329 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:366 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:370 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:377 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:381 +msgid "Lock" +msgstr "Закритий" + +#. line 124 +#. line 125 +#. line 124 +#. line 125 +#. line 124 +#. line 125 +#. line 124 +#. line 125 +#. line 123 +#. line 124 +#. line 123 +#. line 124 +#. line 123 +#. line 124 +#. line 123 +#. line 124 +#. line 123 +#. line 124 +#. line 123 +#. line 124 +#. line 123 +#. line 124 +#. line 136 +#. line 137 +#. line 136 +#. line 137 +#. line 136 +#. line 137 +#. line 136 +#. line 137 +#. line 136 +#. line 137 +#. line 136 +#. line 137 +#. line 136 +#. line 137 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:344 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:348 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:347 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:351 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:343 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:380 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:384 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:391 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:395 +msgid "Raw HTML" +msgstr "Сирий HTML" + +#. line 137 +#. line 136 +#. line 141 +#. line 154 +#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:374 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:377 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:373 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:378 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:415 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:426 +msgid "(For file deletion.)" +msgstr "(для видалення файла)" + +#: ../../../../search.php:5 +msgid "Post search is disabled" +msgstr "Пошук дописів вимкнено" + +#: ../../../../search.php:25 ../../../../search.php:31 +#: ../../../../search.php:29 ../../../../search.php:35 +msgid "Wait a while before searching again, please." +msgstr "Будь ласка, зачекайте трохи, перш ніж знову шукати." + +#: ../../../../search.php:131 ../../../../search.php:135 +msgid "Query too broad." +msgstr "Запит надто загальний." + +#: ../../../../search.php:152 ../../../../search.php:156 +#, php-format +msgid "%d result in" +msgid_plural "%d results in" +msgstr[0] "%d результат у" +msgstr[1] "%d результати у" +msgstr[2] "%d результатів у" + +#: ../../../../search.php:163 ../../../../search.php:167 +msgid "No results." +msgstr "Нема результатів." + +#. line 115 +#. line 16 +#. line 115 +#. line 16 +#. line 115 +#. line 16 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 2 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 2 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 115 +#. line 16 +#. line 115 +#. line 16 +#. line 2 +#. line 13 +#. line 118 +#. line 16 +#. line 2 +#. line 13 +#. line 2 +#. line 118 +#. line 16 +#. line 2 +#. line 13 +#. line 2 +#. line 13 +#. line 118 +#. line 16 +#. line 22 +#. line 2 +#. line 13 +#. line 2 +#: ../../../../search.php:168 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:334 +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:83 +#: ../../../../templates/cache/cb/8b/63013711213735996df92becb7bd43d753c51314cfe5433c562706333eb0.php:25 +#: ../../../../templates/cache/cb/8b/63013711213735996df92becb7bd43d753c51314cfe5433c562706333eb0.php:66 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:25 +#: ../../../../search.php:172 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:343 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:61 +msgid "Search" +msgstr "Пошук" + +#: ../../../../inc/mod/pages.php:939 ../../../../inc/mod/pages.php:936 +#: ../../../../inc/mod/pages.php:941 +msgid "Ban appeal not found!" +msgstr "Оскарження блокування не знайдено!" + +#: ../../../../inc/mod/pages.php:989 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:265 +#: ../../../../inc/mod/pages.php:994 ../../../../inc/mod/pages.php:995 +#: ../../../../inc/mod/pages.php:992 ../../../../inc/mod/pages.php:997 +msgid "Ban appeals" +msgstr "Оскарження блокувань" + +#: ../../../../inc/mod/pages.php:1833 ../../../../inc/mod/pages.php:1849 +#: ../../../../inc/mod/pages.php:1850 ../../../../inc/mod/pages.php:1854 +msgid "New user" +msgstr "Новий користувач" + +#: ../../../../inc/mod/pages.php:1888 ../../../../inc/mod/pages.php:1904 +#: ../../../../inc/mod/pages.php:1905 ../../../../inc/mod/pages.php:1909 +msgid "Impossible to promote/demote user." +msgstr "Неможливо підвищити/понизити користувача." + +#: ../../../../inc/mod/pages.php:2612 ../../../../inc/mod/pages.php:2690 +#: ../../../../inc/mod/pages.php:2684 ../../../../inc/mod/pages.php:2683 +#: ../../../../inc/mod/pages.php:2688 +msgid "Debug: APC" +msgstr "Зневадження: APC" + +#: ../../../../inc/config.php:1026 ../../../../inc/config.php:1017 +#: ../../../../inc/config.php:1019 ../../../../inc/config.php:1021 +#: ../../../../inc/config.php:1037 ../../../../inc/config.php:1046 +#: ../../../../inc/config.php:1077 ../../../../inc/config.php:1081 +#: ../../../../inc/config.php:1084 +msgid "" +"Your code contained PHP syntax errors. Please go back and correct them. PHP " +"says: " +msgstr "" +"Ваш код містить синтаксичні помилки PHP. Будь ласка, поверніться і виправте " +"їх. Повідомлення PHP:" + +#. line 2 +#. line 6 +#. line 2 +#. line 6 +#. line 2 +#. line 46 +#. line 6 +#. line 2 +#. line 46 +#. line 6 +#. line 2 +#. line 46 +#. line 6 +#. line 2 +#. line 46 +#. line 6 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:25 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:38 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:139 +msgid "Boards" +msgstr "Дошки" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:79 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:215 +msgid "edit" +msgstr "редагування" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:97 +msgid "Create new board" +msgstr "Створити нову дошку" + +#. line 32 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:108 +msgid "Messages" +msgstr "Повідомлення" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:188 +msgid "View all noticeboard entries" +msgstr "Переглянути всі записи дошки оголошень" + +#. line 76 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:222 +msgid "Administration" +msgstr "Адміністрування" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:282 +msgid "Change password" +msgstr "Змінити пароль" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:318 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:327 +msgid "Configuration" +msgstr "Конфігурування" + +#. line 127 +#. line 130 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:357 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:366 +msgid "Other" +msgstr "Інше" + +#. line 139 +#. line 142 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:391 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:400 +msgid "Debug" +msgstr "Зневадження" + +#. line 141 +#. line 144 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:396 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:405 +msgid "Anti-spam" +msgstr "Антиспам" + +#. line 142 +#. line 145 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:400 +#: ../../../../inc/mod/pages.php:2288 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:309 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:409 +#: ../../../../inc/mod/pages.php:2283 ../../../../inc/mod/pages.php:2282 +#: ../../../../inc/mod/pages.php:2287 +msgid "Recent posts" +msgstr "Останні дописи" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:407 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:416 +msgid "SQL" +msgstr "SQL" + +#. line 164 +#. line 167 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:446 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:455 +msgid "User account" +msgstr "Обліковий запис користувача" + +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:454 +#: ../../../../templates/cache/72/7e/271125664718133518fd942f20fb724224e100f8a0d47cb0b52f895ac12f.php:463 +msgid "Logout" +msgstr "Вийти" + +#. line 3 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:27 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:27 +msgid "New post" +msgstr "Новий допис" + +#. line 16 +#. line 28 +#. line 16 +#. line 28 +#. line 16 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:55 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:83 +msgid "Body" +msgstr "Вміст" + +#. line 21 +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:63 +msgid "Post to noticeboard" +msgstr "Дописати на дошці оголошень" + +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:90 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:118 +msgid "delete" +msgstr "видалити" + +#: ../../../../templates/cache/39/f9/8228f77b382baf1d61c1215dc0f0236e4cdf8cc5e938259785cf3e67d1ca.php:138 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:123 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:405 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:504 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:251 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:197 +#: ../../../../templates/cache/f8/05/d647b76d6ba28842b313895b0d435295026f1912dc49639bd64e3b42eba3.php:70 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:42 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:67 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:99 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:345 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:415 +msgid "deleted?" +msgstr "видалено?" + +#. line 33 +#: ../../../../templates/cache/1a/7f/6eb467b2d978da59cfea2fe64f8898a5fb769be35fbb2ec50691da9a3d52.php:91 +msgid "Post news entry" +msgstr "Розмістити новину" + +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 67 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 67 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 3 +#. line 67 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 67 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 3 +#. line 67 +#. line 17 +#. line 54 +#. line 133 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 3 +#. line 67 +#. line 17 +#. line 54 +#. line 133 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 67 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 67 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 3 +#. line 67 +#. line 17 +#. line 54 +#. line 133 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 15 +#. line 3 +#. line 67 +#. line 17 +#. line 54 +#. line 133 +#. line 24 +#. line 63 +#. line 152 +#. line 182 +#. line 3 +#. line 67 +#. line 17 +#. line 54 +#. line 133 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:81 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:186 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:391 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:464 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:67 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:183 +#: ../../../../templates/cache/f8/05/d647b76d6ba28842b313895b0d435295026f1912dc49639bd64e3b42eba3.php:26 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:59 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:165 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:371 +msgid "Staff" +msgstr "Модератор" + +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 18 +#. line 25 +#. line 68 +#. line 18 +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 25 +#. line 68 +#. line 18 +#. line 25 +#. line 68 +#. line 18 +#. line 25 +#. line 68 +#. line 18 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:85 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:197 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:63 +msgid "Note" +msgstr "Примітка" + +#. line 26 +#. line 22 +#. line 26 +#. line 19 +#. line 26 +#. line 19 +#. line 26 +#. line 22 +#. line 26 +#. line 19 +#. line 22 +#. line 26 +#. line 19 +#. line 26 +#. line 19 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:89 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:79 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:67 +msgid "Date" +msgstr "Дата" + +#. line 25 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:96 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:88 +msgid "Actions" +msgstr "Дії" + +#. line 49 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:154 +msgid "remove" +msgstr "прибрати" + +#. line 76 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:208 +msgid "New note" +msgstr "Нова примітка" + +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#. line 94 +#. line 7 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:251 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:36 +msgid "Status" +msgstr "Статус" + +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:259 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:44 +msgid "Expired" +msgstr "Спливає" + +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:265 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:50 +msgid "Active" +msgstr "Активний" + +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:299 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:91 +msgid "no reason" +msgstr "без причини" + +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 33 +#. line 118 +#. line 184 +#. line 65 +#. line 33 +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 6 +#. line 33 +#. line 3 +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 33 +#. line 3 +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 6 +#. line 33 +#. line 49 +#. line 136 +#. line 3 +#. line 95 +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 6 +#. line 33 +#. line 49 +#. line 136 +#. line 3 +#. line 118 +#. line 184 +#. line 65 +#. line 3 +#. line 118 +#. line 184 +#. line 65 +#. line 33 +#. line 3 +#. line 118 +#. line 184 +#. line 65 +#. line 33 +#. line 3 +#. line 95 +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 6 +#. line 33 +#. line 49 +#. line 136 +#. line 3 +#. line 95 +#. line 118 +#. line 184 +#. line 65 +#. line 10 +#. line 6 +#. line 33 +#. line 49 +#. line 136 +#. line 3 +#. line 95 +#. line 118 +#. line 184 +#. line 65 +#. line 6 +#. line 33 +#. line 49 +#. line 136 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:309 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:472 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:160 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:47 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:101 +#: ../../../../templates/cache/f8/05/d647b76d6ba28842b313895b0d435295026f1912dc49639bd64e3b42eba3.php:38 +#: ../../../../templates/cache/3a/62/f804928dbcf285e3d5d8d7ae31b1e3a7c78264f270efa9650d31f69c7897.php:26 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:145 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:383 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:263 +msgid "Board" +msgstr "Дошка" + +#. line 71 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:323 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:169 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:133 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:115 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:73 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:100 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:227 +msgid "all boards" +msgstr "всі дошки" + +#. line 128 +#. line 11 +#. line 43 +#. line 128 +#. line 43 +#. line 128 +#. line 11 +#. line 43 +#. line 128 +#. line 11 +#. line 43 +#. line 128 +#. line 11 +#. line 43 +#. line 50 +#. line 128 +#. line 11 +#. line 43 +#. line 50 +#. line 128 +#. line 43 +#. line 128 +#. line 43 +#. line 128 +#. line 11 +#. line 43 +#. line 50 +#. line 128 +#. line 11 +#. line 43 +#. line 50 +#. line 128 +#. line 43 +#. line 50 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:333 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:51 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:125 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:149 +msgid "Set" +msgstr "Встановити" + +#. line 132 +#. line 13 +#. line 47 +#. line 132 +#. line 47 +#. line 132 +#. line 13 +#. line 47 +#. line 132 +#. line 13 +#. line 47 +#. line 132 +#. line 13 +#. line 47 +#. line 52 +#. line 132 +#. line 13 +#. line 47 +#. line 52 +#. line 132 +#. line 47 +#. line 132 +#. line 47 +#. line 132 +#. line 13 +#. line 47 +#. line 52 +#. line 132 +#. line 13 +#. line 47 +#. line 52 +#. line 132 +#. line 47 +#. line 52 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:343 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:59 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:135 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:157 +msgid "Expires" +msgstr "Спливає" + +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:357 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:173 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:149 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:155 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:267 +msgid "never" +msgstr "ніколи" + +#. line 142 +#. line 14 +#. line 57 +#. line 142 +#. line 57 +#. line 142 +#. line 14 +#. line 57 +#. line 142 +#. line 14 +#. line 57 +#. line 142 +#. line 14 +#. line 57 +#. line 53 +#. line 142 +#. line 14 +#. line 57 +#. line 53 +#. line 142 +#. line 57 +#. line 142 +#. line 57 +#. line 142 +#. line 14 +#. line 57 +#. line 53 +#. line 142 +#. line 14 +#. line 57 +#. line 53 +#. line 142 +#. line 57 +#. line 53 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:367 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:63 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:159 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:161 +msgid "Seen" +msgstr "Побачено" + +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:375 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:201 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:167 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:295 +msgid "Yes" +msgstr "Так" + +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:381 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:207 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:173 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:301 +msgid "No" +msgstr "Ні" + +#. line 163 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:419 +msgid "Remove ban" +msgstr "Прибрати блокування" + +#. line 183 +#. line 5 +#. line 183 +#. line 5 +#. line 135 +#. line 94 +#. line 183 +#. line 5 +#. line 135 +#. line 183 +#. line 94 +#. line 183 +#. line 5 +#. line 135 +#. line 94 +#. line 183 +#. line 5 +#. line 135 +#. line 94 +#. line 183 +#. line 5 +#. line 135 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:468 +#: ../../../../templates/cache/f8/05/d647b76d6ba28842b313895b0d435295026f1912dc49639bd64e3b42eba3.php:34 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:379 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:259 +msgid "Time" +msgstr "Час" + +#. line 185 +#. line 89 +#. line 185 +#. line 89 +#. line 185 +#. line 7 +#. line 89 +#. line 185 +#. line 89 +#. line 185 +#. line 7 +#. line 89 +#. line 137 +#. line 96 +#. line 185 +#. line 7 +#. line 89 +#. line 137 +#. line 185 +#. line 89 +#. line 185 +#. line 89 +#. line 96 +#. line 185 +#. line 7 +#. line 89 +#. line 137 +#. line 96 +#. line 185 +#. line 7 +#. line 89 +#. line 137 +#. line 96 +#. line 185 +#. line 7 +#. line 89 +#. line 137 +#: ../../../../templates/cache/b1/4c/16a427b0d49ecf353c259d9fb606841783484eca9d790e766fdf0e3e9754.php:476 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:234 +#: ../../../../templates/cache/f8/05/d647b76d6ba28842b313895b0d435295026f1912dc49639bd64e3b42eba3.php:42 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:387 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:267 +msgid "Action" +msgstr "Дія" + +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:73 +msgid "(or subnet)" +msgstr "(або підмережа)" + +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:88 +msgid "hidden" +msgstr "прихований" + +#. line 41 +#. line 27 +#. line 41 +#. line 27 +#. line 41 +#. line 27 +#. line 41 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:117 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:92 +msgid "Message" +msgstr "Повідомлення" + +#. line 46 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:133 +msgid "public; attached to post" +msgstr "" + +#. line 58 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:150 +msgid "Length" +msgstr "Довжина" + +#. line 88 +#: ../../../../templates/cache/88/92/8e730a689121629afa3d2c0f374e1f246baa76e7cf0f3ec680f51805eccd.php:212 +msgid "New Ban" +msgstr "Нове блокування" + +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#. line 2 +#. line 5 +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:25 +#: ../../../../templates/cache/cb/8b/63013711213735996df92becb7bd43d753c51314cfe5433c562706333eb0.php:31 +msgid "Phrase:" +msgstr "Фраза:" + +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:38 +msgid "Posts" +msgstr "Дописи" + +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:49 +msgid "IP address notes" +msgstr "Примітки до IP адреси" + +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:62 +msgid "Bans" +msgstr "Блокування" + +#. line 18 +#: ../../../../templates/cache/73/f8/5e3142a8a6f8d7e40422ff577e83b0dedf55a7cb9bc7082839b24f653545.php:88 +msgid "" +"(Search is case-insensitive and based on keywords. To match exact phrases, " +"use \"quotes\". Use an asterisk (*) for wildcard.)" +msgstr "" +"(Пошук ведеться за ключовими словами і є нечутливим до регістру. Щоб знайти " +"конкретні фрази, використовуйте \"лапки\". Для масок використовуйте зірочку " +"(*)." + +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:25 +msgid "There are no active bans." +msgstr "Немає чинних блокувань." + +#. line 8 +#. line 47 +#. line 8 +#. line 47 +#. line 8 +#. line 47 +#. line 8 +#. line 47 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:39 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:137 +msgid "IP address/mask" +msgstr "IP адреса/маска" + +#. line 12 +#. line 51 +#. line 12 +#. line 51 +#. line 12 +#. line 51 +#. line 12 +#. line 51 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:55 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:153 +msgid "Duration" +msgstr "Термін" + +#. line 92 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:269 +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:68 +msgid "Unban selected" +msgstr "Розблокувати вибраних" + +#. line 6 +#. line 4 +#. line 6 +#. line 4 +#. line 11 +#. line 6 +#. line 4 +#. line 6 +#. line 11 +#. line 6 +#. line 4 +#. line 11 +#. line 6 +#. line 4 +#. line 11 +#. line 6 +#. line 4 +#: ../../../../templates/cache/00/31/a027d7b6d57819b6e43e58620f3f4c76194dd75db65fc888a5053ce62803.php:34 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:30 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:45 +msgid "Username" +msgstr "Імʼя користувача" + +#. line 23 +#: ../../../../templates/cache/00/31/a027d7b6d57819b6e43e58620f3f4c76194dd75db65fc888a5053ce62803.php:60 +msgid "Continue" +msgstr "Продовжити" + +#. line 80 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:210 +msgid "Appeal time" +msgstr "Час для оскарження" + +#. line 84 +#: ../../../../templates/cache/03/13/62c259daae13f7b39b689162b7cd380b2673bee7e05b90f0d34b69a01190.php:220 +msgid "Appeal reason" +msgstr "Причина оскарження" + +#: ../../../../templates/cache/7d/63/b6fd83bf4ed7f6031a2b3373b997d2d40617bf98899fe672a0aae48520c5.php:31 +msgid "There are no reports." +msgstr "Немає скарг." + +#: ../../../../post.php:802 ../../../../post.php:811 ../../../../post.php:825 +#: ../../../../post.php:894 ../../../../post.php:896 +msgid "That ban doesn't exist or is not for you." +msgstr "Це блокування не існує або його застосовано не до вас." + +#: ../../../../post.php:806 ../../../../post.php:815 ../../../../post.php:829 +#: ../../../../post.php:898 ../../../../post.php:900 +msgid "You cannot appeal a ban of this length." +msgstr "Ви не можете оскаржити блокування з таким терміном." + +#: ../../../../post.php:813 ../../../../post.php:822 ../../../../post.php:836 +#: ../../../../post.php:905 ../../../../post.php:907 +msgid "You cannot appeal this ban again." +msgstr "Ви не можете оскаржити це блокування знову." + +#: ../../../../post.php:818 ../../../../post.php:827 ../../../../post.php:841 +#: ../../../../post.php:910 ../../../../post.php:912 +msgid "There is already a pending appeal for this ban." +msgstr "Оскарження цього блокування вже розглядається." + +#: ../../../../inc/image.php:24 ../../../../inc/image.php:62 +msgid "Unsupported file format: " +msgstr "Непідтримуваний формат файла:" + +#: ../../../../inc/image.php:282 ../../../../inc/image.php:288 +msgid "Failed to redraw image!" +msgstr "Не вдалося перемалювати зображення!" + +#: ../../../../inc/image.php:324 ../../../../inc/image.php:343 +#: ../../../../inc/image.php:368 ../../../../inc/image.php:342 +#: ../../../../inc/image.php:366 ../../../../inc/image.php:372 +msgid "Failed to resize image!" +msgstr "Не вдалося змінити розмір зображення!" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:35 +msgid "You were banned! ;_;" +msgstr "Вас було заблоковано! ;_;" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:41 +msgid "You are banned! ;_;" +msgstr "Вас заблоковано! ;_;" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:52 +msgid "You were banned from" +msgstr "Вас було заблоковано на" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:58 +msgid "You have been banned from" +msgstr "Вас заблоковано на" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:82 +msgid "for the following reason:" +msgstr "з такої причини:" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:88 +msgid "for an unspecified reason." +msgstr ", причину не вказано." + +#. line 32 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:110 +msgid "Your ban was filed on" +msgstr "" + +#. line 51 +#. line 59 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:123 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:156 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:148 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:181 +msgid "has since expired. Refresh the page to continue." +msgstr "сплив. Оновіть сторінку, аби продовжити." + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:129 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:154 +msgid "expires" +msgstr "спливає" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:133 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:158 +msgid "from now, which is on" +msgstr "" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:183 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:208 +msgid "will not expire" +msgstr "не сплине" + +#. line 78 +#. line 86 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:192 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:217 +msgid "Your IP address is" +msgstr "Ваша IP адреса" + +#. line 86 +#. line 94 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:215 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:240 +msgid "You were banned for the following post on" +msgstr "Вас було заблоковано за цей допис" + +#. line 95 +#. line 103 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:239 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:240 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:265 +msgid "You submitted an appeal for this ban on" +msgstr "Ви подали оскарження цього блокування" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:245 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:246 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:271 +msgid "It is still pending" +msgstr "Воно досі розглядається" + +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 101 +#. line 112 +#. line 109 +#. line 120 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:257 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:289 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:258 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:290 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:283 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:315 +msgid "You appealed this ban on" +msgstr "Ви оскаржили це блокування" + +#. line 103 +#. line 111 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:265 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:266 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:291 +msgid "and it was denied. You may not appeal this ban again." +msgstr ". Оскарження відхилено. Ви не можете знову оскаржувати це блокування." + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:272 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:273 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:298 +msgid "" +"You have submitted the maximum number of ban appeals allowed. You may not " +"appeal this ban again." +msgstr "" +"Ви подали максимальну кількість оскаржень, і більше не можете оскаржувати це " +"блокування." + +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 114 +#. line 121 +#. line 122 +#. line 129 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:297 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:318 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:298 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:319 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:323 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:344 +msgid "and it was denied." +msgstr ". Оскарження відхилено." + +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 116 +#. line 123 +#. line 124 +#. line 131 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:302 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:323 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:303 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:324 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:328 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:349 +msgid "You may appeal this ban again. Please enter your reasoning below." +msgstr "" +"Ви можете оскаржити це блокування ще раз. Будь ласка, обґрунтуйте його " +"несправедливість тут." + +#. line 119 +#. line 127 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:310 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:311 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:336 +msgid "You last appealed this ban on" +msgstr "Востаннє ви оскаржували це блокування" + +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:332 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:333 +#: ../../../../templates/cache/b7/ae/f9663c9ca58d1e218de29e04d0fa229f6263f4e68b613ce608bc023897a2.php:358 +msgid "You may appeal this ban. Please enter your reasoning below." +msgstr "" +"Ви можете оскаржити це блокування. Будь ласка, обґрунтуйте його " +"несправедливість тут." + +#. line 4 +#. line 16 +#. line 134 +#. line 93 +#. line 4 +#. line 16 +#. line 134 +#. line 93 +#. line 4 +#. line 16 +#. line 134 +#. line 93 +#. line 4 +#. line 16 +#. line 134 +#. line 93 +#. line 4 +#. line 16 +#. line 134 +#: ../../../../templates/cache/f8/05/d647b76d6ba28842b313895b0d435295026f1912dc49639bd64e3b42eba3.php:30 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:55 +#: ../../../../templates/cache/6a/a4/b13523024ba2660a4f8f05e0c909c55477f675db9a2620075762ac245c91.php:375 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:255 +msgid "IP address" +msgstr "IP адреса" + +#. line 3 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:26 +msgid "ID" +msgstr "ID" + +#. line 5 +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:34 +msgid "Type" +msgstr "Тип" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:45 +msgid "Last action" +msgstr "Остання дія" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:80 +msgid "Unknown" +msgstr "Невідомо" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:94 +msgid "none" +msgstr "немає" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:174 +msgid "Promote" +msgstr "Підвищити" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:187 +msgid "Demote" +msgstr "Понизити" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:191 +msgid "Are you sure you want to demote yourself?" +msgstr "Ви впевнені, що хочете себе понизити" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:204 +msgid "log" +msgstr "журнал" + +#: ../../../../templates/cache/fc/8d/2b5f6c25d93a9966c429a79ee7ebdd921957079dab214aebbc665d67b9f4.php:226 +msgid "PM" +msgstr "Приватне повідомлення" + +#. line 6 +#: ../../../../templates/cache/32/3a/d7e02cef5846ec4f1f423bb0ad2d3c307845d29f70da3f8a90a41f873e7d.php:36 +msgid "Thread ID" +msgstr "ID нитки" + +#. line 14 +#: ../../../../templates/cache/32/3a/d7e02cef5846ec4f1f423bb0ad2d3c307845d29f70da3f8a90a41f873e7d.php:51 +msgid "Leave shadow thread" +msgstr "" + +#. line 18 +#: ../../../../templates/cache/32/3a/d7e02cef5846ec4f1f423bb0ad2d3c307845d29f70da3f8a90a41f873e7d.php:58 +msgid "locks thread; replies to it with a link." +msgstr "закриває нитку, відповідає туди посиланням." + +#. line 22 +#. line 13 +#. line 22 +#. line 13 +#. line 22 +#. line 13 +#: ../../../../templates/cache/32/3a/d7e02cef5846ec4f1f423bb0ad2d3c307845d29f70da3f8a90a41f873e7d.php:65 +#: ../../../../templates/cache/56/25/ac2c51fa6b3e26f9f9ed7dda5224acfbec96881d648c8ded10c5eef2c3e5.php:50 +msgid "Target board" +msgstr "Дошка призначення" + +#. line 8 +#: ../../../../templates/cache/cb/8b/63013711213735996df92becb7bd43d753c51314cfe5433c562706333eb0.php:40 +msgid "Select board" +msgstr "Вибрати дошку" + +#. line 17 +#: ../../../../templates/cache/cb/8b/63013711213735996df92becb7bd43d753c51314cfe5433c562706333eb0.php:73 +msgid "" +"Search is case-insensitive and based on keywords. To match exact phrases, " +"use \"quotes\". Use an asterisk (*) for wildcard.

    You may apply the following filters to your searches: " +"id, thread, subject, and " +"name. To apply a filter, simply add to your query, for " +"example, name:Anonymous or subject:\"Some Thread\". " +"Wildcards cannot be used in filters." +msgstr "" +"(Пошук ведеться за ключовими словами і є нечутливим до регістру. Щоб знайти " +"конкретні фрази, використовуйте \"лапки\". Для масок використовуйте зірочку " +"(*).

    Ви можете застосувати до свого " +"пошуку такі фільтри: id, нитка, " +"тема та імʼя. Щоб застосувати фільтр, " +"просто додайте його до запиту, наприклад, імʼя:Хтось або тема:" +"\"Якась нитка\". У фільтрах не можна використовувати маску." + +#. line 2 +#: ../../../../templates/cache/57/a7/c0a734e494c78acfc595f033a070bdc87fdc3e6a28ad5aaa8666c7a1a966.php:25 +msgid "Are you sure you want to do that?" +msgstr "Ви впевнені, що хочете це зробити?" + +#: ../../../../templates/cache/57/a7/c0a734e494c78acfc595f033a070bdc87fdc3e6a28ad5aaa8666c7a1a966.php:31 +msgid "Click to proceed to" +msgstr "Клацніть, щоб перейти до" + +#. line 5 +#: ../../../../templates/cache/57/a7/c0a734e494c78acfc595f033a070bdc87fdc3e6a28ad5aaa8666c7a1a966.php:39 +msgid "" +"You are probably seeing this message because Javascript being disabled. This " +"is a necessary security measure to prevent CSRF attacks." +msgstr "" +"Найімовірніше, ви бачите це повідомлення тому, що Javascript вимкнено. Його " +"повинно бути ввімкнено для захисту від міжсайтової підробки запиту." + +#. line 7 +#: ../../../../templates/cache/3a/62/f804928dbcf285e3d5d8d7ae31b1e3a7c78264f270efa9650d31f69c7897.php:44 +msgid "Report date" +msgstr "Дата скарги" + +#: ../../../../templates/cache/3a/62/f804928dbcf285e3d5d8d7ae31b1e3a7c78264f270efa9650d31f69c7897.php:54 +msgid "Reported by" +msgstr "Хто поскаржився" + +#: ../../../../templates/cache/3a/62/f804928dbcf285e3d5d8d7ae31b1e3a7c78264f270efa9650d31f69c7897.php:73 +msgid "Discard abuse report" +msgstr "Відхилити скарги на порушення правил" + +#: ../../../../templates/cache/3a/62/f804928dbcf285e3d5d8d7ae31b1e3a7c78264f270efa9650d31f69c7897.php:93 +msgid "Discard all abuse reports by this IP address" +msgstr "Відхилити всі скарги на порушення правил від цієї IP адреси" + +#. line 4 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:27 +msgid "From" +msgstr "Від" + +#. line 34 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:105 +msgid "Delete forever" +msgstr "Видалити назавжди" + +#. line 39 +#: ../../../../templates/cache/f9/e9/d592e3c89e2f76520cf989aa8359d3d143d8fa4996ff1d97b3be51f87a05.php:119 +msgid "Reply with quote" +msgstr "Відповісти цитатою" + +#. line 18 +#: ../../../../templates/cache/1f/f5/c63468797b4f93a8005563716a720117a6d51a804f2124a4c5158ca78525.php:62 +msgid "Send message" +msgstr "Надіслати повідомлення" + +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:25 +msgid "There are no themes available." +msgstr "Немає доступних тем." + +#. line 11 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:50 +msgid "Version" +msgstr "Версія" + +#. line 15 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:60 +msgid "Description" +msgstr "Опис" + +#. line 19 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:70 +msgid "Thumbnail" +msgstr "Мініатюра" + +#. line 27 +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:93 +msgid "Use theme" +msgstr "Використовувати тему" + +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:100 +msgid "Reconfigure" +msgstr "Переконфігурувати" + +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:102 +msgid "Install" +msgstr "Встановити" + +#: ../../../../templates/cache/ae/30/5b1888bb2e8ab6981af945fea88c1ecb021b0dfa8a068ee7adeb9dd3ee7d.php:123 +msgid "Uninstall" +msgstr "Видалити" + +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:72 +msgid "new; optional" +msgstr "нова; необовʼязкова" + +#. line 32 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:98 +msgid "Group" +msgstr "Група" + +#. line 56 +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:161 +msgid "All boards" +msgstr "Всі дошки" + +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:223 +msgid "Create user" +msgstr "Створити користувача" + +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:229 +msgid "Save changes" +msgstr "Зберегти зміни" + +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:236 +msgid "Delete user" +msgstr "Видалити користувача" + +#: ../../../../templates/cache/37/ea/10898251a344348e062662ce7a7b7f6c8dae001e2c860ce58ea35cedd935.php:331 +msgid "View more logs for this user." +msgstr "Переглянути більше записів журналу щодо цього користувача." + +#. line 84 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:255 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:266 +msgid "Flag" +msgstr "Позначити" + +#. line 87 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:261 +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:272 +msgid "None" +msgstr "Немає" + +#. When moving a thread to another board and choosing to keep a "shadow thread", an automated post (with +#. a capcode) will be made, linking to the new location for the thread. "%s" will be replaced with a +#. standard cross-board post citation (>>>/board/xxx) +#: ../../../../inc/config.php:1211 ../../../../inc/config.php:1220 +#: ../../../../inc/config.php:1251 ../../../../inc/config.php:1255 +#: ../../../../inc/config.php:1258 +#, php-format +msgid "Moved to %s." +msgstr "Переміщено до %s." + +#: ../../../../templates/themes/recent/theme.php:50 +msgid "" +"Can't build the RecentPosts theme, because there are no boards to be fetched." +msgstr "" +"Неможливо зібрати тему RecentPosts, тому що немає з яких дошок їх зібрати." + +#: ../../../../inc/config.php:997 ../../../../inc/config.php:1023 +#: ../../../../inc/config.php:1026 ../../../../inc/config.php:1029 +msgid "You have attempted to upload too many images!" +msgstr "Ви спробували завантажити надто багато зображень!" + +#. line 7 +#: ../../../../templates/cache/b9/2b/ba2b45df5e1d76f6cdfb98a47468df19a1ffc1c2af2dc1792eb75eeb0791.php:36 +msgid "Spoiler file" +msgstr "Заховати файл під спойлер" + +#. line 26 +#. line 35 +#. line 26 +#. line 35 +#. line 20 +#. line 28 +#. line 20 +#. line 28 +#. line 20 +#. line 28 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:80 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:107 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:67 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:90 +msgid "Bump order" +msgstr "порядком підіймання" + +#. line 27 +#. line 36 +#. line 27 +#. line 36 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:84 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:111 +msgid "Last reply" +msgstr "Остання відповідь" + +#. line 28 +#. line 37 +#. line 28 +#. line 37 +#. line 21 +#. line 29 +#. line 21 +#. line 29 +#. line 21 +#. line 29 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:88 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:115 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:71 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:94 +msgid "Creation date" +msgstr "датою створення" + +#. line 29 +#. line 38 +#. line 29 +#. line 38 +#. line 22 +#. line 30 +#. line 22 +#. line 30 +#. line 22 +#. line 30 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:92 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:119 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:75 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:98 +msgid "Reply count" +msgstr "кількістю відповідей" + +#. line 30 +#. line 39 +#. line 30 +#. line 39 +#. line 23 +#. line 31 +#. line 23 +#. line 31 +#. line 23 +#. line 31 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:96 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:123 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:79 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:102 +msgid "Random" +msgstr "довільно" + +#. line 33 +#. line 26 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:102 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:85 +msgid "Sort by" +msgstr "Сортувати за" + +#. line 42 +#. line 34 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:129 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:108 +msgid "Image size" +msgstr "Розмір зображення" + +#. line 44 +#. line 37 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:134 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:117 +msgid "Small" +msgstr "Маленьке" + +#. line 45 +#. line 38 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:138 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:121 +msgid "Large" +msgstr "Велике" + +#. line 6 +#: ../../../../templates/cache/56/25/ac2c51fa6b3e26f9f9ed7dda5224acfbec96881d648c8ded10c5eef2c3e5.php:36 +msgid "Post ID" +msgstr "ID допису" + +#. line 29 +#: ../../../../templates/cache/56/25/ac2c51fa6b3e26f9f9ed7dda5224acfbec96881d648c8ded10c5eef2c3e5.php:96 +msgid "Target thread" +msgstr "Цільовий допис" + +#: ../../../../post.php:48 +msgid "Post deletion is not allowed!" +msgstr "Видалення допису не дозволено!" + +#: ../../../../post.php:385 +msgid "Unrecognized file size determination method." +msgstr "Невідомий метод визначення розміру файла." + +#: ../../../../post.php:519 +msgid "Invalid flag selection!" +msgstr "Неправильний вибір позначок!" + +#: ../../../../post.php:631 ../../../../post.php:633 +msgid "exiftool failed!" +msgstr "exiftool не спрацювала!" + +#: ../../../../post.php:641 ../../../../post.php:643 +msgid "Could not auto-orient image!" +msgstr "Не вдалося автоматично обернути зображення!" + +#: ../../../../post.php:695 ../../../../post.php:697 +msgid "Could not strip EXIF metadata!" +msgstr "Не вдалося очистити файл від даних EXIF!" + +#: ../../../../inc/config.php:1049 ../../../../inc/config.php:1052 +#: ../../../../inc/config.php:1053 ../../../../inc/config.php:1055 +#: ../../../../inc/config.php:1056 +msgid "There was a problem processing your webm." +msgstr "Під час обробки вашої webm сталася помилка." + +#. Is this error used anywhere ? +#: ../../../../inc/config.php:1050 ../../../../inc/config.php:1054 +#: ../../../../inc/config.php:1057 +msgid "Invalid webm uploaded." +msgstr "Завантажено хибну webm." + +#: ../../../../inc/config.php:1051 ../../../../inc/config.php:1055 +#: ../../../../inc/config.php:1058 +msgid "" +"The uploaded webm contains an audio or another type of additional stream." +msgstr "Завантажена webm містить аудіо або ще один потік даних іншого типу." + +#: ../../../../inc/config.php:1052 ../../../../inc/config.php:1056 +#: ../../../../inc/config.php:1059 +msgid "The uploaded webm is longer than " +msgstr "Завантажена webm довша, ніж" + +#. line 48 +#: ../../../../templates/cache/b8/d9/05d4f2709538c393e80cdee33c24efe8d6fd13e68df2f5b3493356ef42e3.php:190 +msgid "Go to top" +msgstr "На початок сторінки" + +#: ../../../../templates/cache/cf/63/151e140d85df674832f4ede3f3e7811b97d4efa91cac6086ca7e8ce65d25.php:80 +msgid "Don't show my flag" +msgstr "Не показувати мої позначки" + +#. line 36 +#: ../../../../templates/cache/41/57/9143de5f74d921965e5ff24e0f1ce44a18317fd4937f5d8d65f56337acf3.php:113 +msgid "Very small" +msgstr "Дуже маленький" + +#: ../../../../templates/themes/recent/theme.php:99 +msgid "(no comment)" +msgstr "(коментар відсутній)" + +#: ../../../../templates/cache/3a/fa/a15b273b182a2c833d42475af728873be4a7cb0481854a52be6ed9380ea2.php:30 +msgid "There are no active posts." +msgstr "Немає активних дописів." + +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:49 +msgid "Show only bans from boards I moderate" +msgstr "Показати блокування тільки з тих дошок, які я модерую" + +#: ../../../../templates/cache/ba/55/2553cc018aecf7d29a62331aec4bedc71b646817c7e4c4e7d1a885263676.php:55 +msgid "Show only active bans" +msgstr "Показати тільки чинні блокування" From e672cf9ba1b80d00c56c35ae73a627753316dd2b Mon Sep 17 00:00:00 2001 From: tlm-2501 <13thSlayer@gmail.com> Date: Mon, 23 Jan 2017 21:15:13 +0300 Subject: [PATCH 174/212] Show backlinks on OP's as well, and disable the script on Ukko boards (it's broken there anyway) --- js/show-backlinks.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/js/show-backlinks.js b/js/show-backlinks.js index fa4865fd..b52434ee 100644 --- a/js/show-backlinks.js +++ b/js/show-backlinks.js @@ -14,8 +14,9 @@ */ onready(function(){ + if($('body').hasClass('active-ukko')) return; var showBackLinks = function() { - var reply_id = $(this).attr('id').replace(/^reply_/, ''); + var reply_id = $(this).attr('id').replace(/(^reply_)|(^op_)/, ''); $(this).find('div.body a:not([rel="nofollow"])').each(function() { var id, post, $mentioned; @@ -26,8 +27,11 @@ onready(function(){ return; $post = $('#reply_' + id); - if($post.length == 0) - return; + if($post.length == 0){ + $post = $('#op_' + id); + if($post.length == 0) + return; + } $mentioned = $post.find('p.intro span.mentioned'); if($mentioned.length == 0) @@ -47,14 +51,12 @@ onready(function(){ }; $('div.post.reply').each(showBackLinks); + $('div.post.op').each(showBackLinks); $(document).on('new_post', function(e, post) { - if ($(post).hasClass("reply")) { - showBackLinks.call(post); - } - else { + showBackLinks.call(post); + if ($(post).hasClass("op")) { $(post).find('div.post.reply').each(showBackLinks); } }); }); - From 387ebe9c0c211867a3037b9fb0756ea4db6c32bb Mon Sep 17 00:00:00 2001 From: vholmes Date: Wed, 15 Feb 2017 23:07:50 -0200 Subject: [PATCH 175/212] Prevents reports with too many characters --- inc/config.php | 1 + post.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/inc/config.php b/inc/config.php index 6d1a8081..b17f55de 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1103,6 +1103,7 @@ $config['error']['toomanycross'] = _('Too many cross-board links; post discarded.'); $config['error']['nodelete'] = _('You didn\'t select anything to delete.'); $config['error']['noreport'] = _('You didn\'t select anything to report.'); + $config['error']['invalidreport'] = _('The reason was too long.'); $config['error']['toomanyreports'] = _('You can\'t report that many posts at once.'); $config['error']['invalidpassword'] = _('Wrong password…'); $config['error']['invalidimg'] = _('Invalid image.'); diff --git a/post.php b/post.php index 47c5f0ef..01b2ef6e 100644 --- a/post.php +++ b/post.php @@ -287,6 +287,9 @@ if (isset($_POST['delete'])) { if (empty($report)) error($config['error']['noreport']); + + if (strlen($report) > 30) + error($config['error']['invalidreport']); if (count($report) > $config['report_limit']) error($config['error']['toomanyreports']); From d85cb47647ec5333c196a105f6d48a4df7b5967b Mon Sep 17 00:00:00 2001 From: czaks Date: Sat, 11 Mar 2017 08:34:58 -0500 Subject: [PATCH 176/212] report event --- post.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/post.php b/post.php index 47c5f0ef..2989f910 100644 --- a/post.php +++ b/post.php @@ -310,7 +310,7 @@ if (isset($_POST['delete'])) { $reason = escape_markup_modifiers($_POST['reason']); markup($reason); - + foreach ($report as &$id) { $query = prepare(sprintf("SELECT `id`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); @@ -318,6 +318,12 @@ if (isset($_POST['delete'])) { $post = $query->fetch(PDO::FETCH_ASSOC); + $error = event('report', array('ip' => $_SERVER['REMOTE_ADDR'], 'board' => $board['uri'], 'post' => $post, 'reason' => $reason)); + + if ($error) { + error($error); + } + if ($config['syslog']) _syslog(LOG_INFO, 'Reported post: ' . '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') . From 57732bdff5afa169d132af6bd08879428edfe32c Mon Sep 17 00:00:00 2001 From: KekuKin Date: Thu, 30 Mar 2017 00:58:11 +0200 Subject: [PATCH 177/212] Fixed uninstall error for themes. Was receiving uninstall errors: undefined index: theme --- inc/mod/pages.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index c3e98af5..9167cfa0 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -2634,7 +2634,7 @@ function mod_theme_uninstall($theme_name) { // Clean cache Cache::delete("themes"); - Cache::delete("theme_settings_".$theme); + Cache::delete("theme_settings_".$theme_name); header('Location: ?/themes', true, $config['redirect_http']); } From 175b54b7f05129c130b0abec9f8db4af38495969 Mon Sep 17 00:00:00 2001 From: Horija Date: Fri, 21 Apr 2017 03:09:48 +0200 Subject: [PATCH 178/212] Fixed go to bottom link --- templates/thread.html | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/thread.html b/templates/thread.html index 88e6ec6d..8a452d52 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -77,6 +77,7 @@

    + {{ boardlist.bottom }} {{ config.ad.bottom }} From 9ddb5833b3ddac50f326ffa1ff0c6b32b5a2abee Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:38:56 +0200 Subject: [PATCH 179/212] Implementing a new captcha I'm (trying) to integrate @Czaks custom captcha --- inc/config.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/inc/config.php b/inc/config.php index 6d1a8081..c9b87c8e 100644 --- a/inc/config.php +++ b/inc/config.php @@ -284,6 +284,8 @@ 'embed', 'recaptcha_challenge_field', 'recaptcha_response_field', + 'captcha_cookie', + 'captcha_text', 'spoiler', 'page', 'file_url', @@ -299,6 +301,26 @@ // Public and private key pair from https://www.google.com/recaptcha/admin/create $config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f'; $config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_'; + + // Enable Custom Captcha you need to change a couple of settings + //Read more at: /captcha/instructions.md + $config['captcha'] = array(); + + // Enable custom captcha provider + $config['captcha']['enabled'] = false; + + //New thread captcha + //Require solving a captcha to post a thread. + //Default off. + $config['new_thread_capt'] = false; + + // Custom captcha get provider path (if not working get the absolute path aka your url.) + $config['captcha']['provider_get'] = '../inc/captcha/entrypoint.php'; + // Custom captcha check provider path + $config['captcha']['provider_check'] = '../inc/captcha/entrypoint.php'; + + // Custom captcha extra field (eg. charset) + $config['captcha']['extra'] = 'abcdefghijklmnopqrstuvwxyz'; // Ability to lock a board for normal users and still allow mods to post. Could also be useful for making an archive board $config['board_locked'] = false; @@ -1014,6 +1036,7 @@ // $config['additional_javascript'][] = 'js/auto-reload.js'; // $config['additional_javascript'][] = 'js/post-hover.js'; // $config['additional_javascript'][] = 'js/style-select.js'; + // $config['additional_javascript'][] = 'js/captcha.js'; // Where these script files are located on the web. Defaults to $config['root']. // $config['additional_javascript_url'] = 'http://static.example.org/tinyboard-javascript-stuff/'; From 6a53b99feb4fc07d887244790c9cc0cb007ca034 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:40:22 +0200 Subject: [PATCH 180/212] Create readme.md --- inc/captcha/readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 inc/captcha/readme.md diff --git a/inc/captcha/readme.md b/inc/captcha/readme.md new file mode 100644 index 00000000..8b1f538a --- /dev/null +++ b/inc/captcha/readme.md @@ -0,0 +1,10 @@ +I integrated this from: https://github.com/ctrlcctrlv/infinity/commit/62a6dac022cb338f7b719d0c35a64ab3efc64658 + +First import the captcha/dbschema.sql in your database it is no longer required. + +In inc/captcha/config.php change the database_name database_user database_password to your own settings. + +Add js/captcha.js in your instance-config.php or config.php + +Go to Line 305 in the /inc/config file and copy the settings in instance config, while changing the url to your website. +Go to the line beneath it if you only want to enable it when posting a new thread. From 34387186676b1f410d15b3ad5b46e25760804369 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:40:49 +0200 Subject: [PATCH 181/212] Add files via upload --- inc/captcha/captcha.php | 357 +++++++++++++++++++++++++++++++++++++ inc/captcha/config.php | 16 ++ inc/captcha/dbschema.sql | 9 + inc/captcha/entrypoint.php | 85 +++++++++ 4 files changed, 467 insertions(+) create mode 100644 inc/captcha/captcha.php create mode 100644 inc/captcha/config.php create mode 100644 inc/captcha/dbschema.sql create mode 100644 inc/captcha/entrypoint.php diff --git a/inc/captcha/captcha.php b/inc/captcha/captcha.php new file mode 100644 index 00000000..7c54fa3c --- /dev/null +++ b/inc/captcha/captcha.php @@ -0,0 +1,357 @@ +width = $left; + $this->height = $top; + + $this->charset = preg_split('//u', $charset); + + $this->style = ""; + + for ($i = 0; $i < $len; $i++) { + $this->content[] = array(mb_substr($text, $i, 1, 'utf-8'), "top" => $top / 2 - $top / 4, + "left" => $left/10 + 9*$left*$i/10/$len, + "position" => "absolute"); + } + + $this->color = "hsla(".rand(1,360).", 76%, 78%, 1)"; + + $this->add_junk(); + $this->mutate_sizes(); + $this->mutate_positions(); + $this->mutate_transform(); + $this->mutate_anchors(); + $this->randomize(); + $this->mutate_containers(); + $this->mutate_margins(); + $this->mutate_styles(); + $this->randomize(); + } + + function mutate_sizes() { + foreach ($this->content as &$v) { + if (!isset ($v['font-size'])) + $v['font-size'] = rand($this->height/3 - 4, $this->height/3 + 8); + } + } + function mutate_positions() { + foreach ($this->content as &$v) { + $v['top'] += rand(-10,10); + $v['left'] += rand(-10,10); + } + } + function mutate_transform() { + $fromto = array('6'=>'9', '9'=>'6', '8'=>'8', '0'=>'0', + 'z'=>'z', 's'=>'s', 'n'=>'u', 'u'=>'n', + 'a'=>'ɐ', 'e'=>'ə', 'p'=>'d', 'd'=>'p', + 'A'=>'∀', 'E'=>'∃', 'H'=>'H', 'o'=>'o', + 'O'=>'O'); + + foreach ($this->content as &$v) { + $basefrom = -20; + $baseto = 20; + + if (isset($fromto[$v[0]]) && rand(0,1)) { + $v[0] = $fromto[$v[0]]; + $basefrom = 160; + $baseto = 200; + } + + $v['transform'] = 'rotate('.rand($basefrom,$baseto).'deg)'; + $v['-ms-transform'] = 'rotate('.rand($basefrom,$baseto).'deg)'; + $v['-webkit-transform'] = 'rotate('.rand($basefrom,$baseto).'deg)'; + } + } + function randomize(&$a = false) { + if ($a === false) { + $a = &$this->content; + } + + shuffle($a); + + foreach ($a as &$v) { + $this->shuffle_assoc($v); + + if (is_array ($v[0])) { + $this->randomize($v[0]); + } + } + } + + function add_junk() { + $count = rand(200, 300); + + while ($count--) { + $elem = array(); + + $elem['top'] = rand(0, $this->height); + $elem['left'] = rand(0, $this->width); + + $elem['position'] = 'absolute'; + + $elem[0] = $this->charset[rand(0, count($this->charset)-1)]; + + switch($t = rand (0,9)) { + case 0: + $elem['display'] = 'none'; break; + case 1: + $elem['top'] = rand(-60, -90); break; + case 2: + $elem['left'] = rand(-40, -70); break; + case 3: + $elem['top'] = $this->height + rand(10, 60); break; + case 4: + $elem['left'] = $this->width + rand(10, 60); break; + case 5: + $elem['color'] = $this->color; break; + case 6: + $elem['visibility'] = 'hidden'; break; + case 7: + $elem['height'] = rand(0,2); + $elem['overflow'] = 'hidden'; break; + case 8: + $elem['width'] = rand(0,1); + $elem['overflow'] = 'hidden'; break; + case 9: + $elem['font-size'] = rand(2, 6); break; + } + + $this->content[] = $elem; + } + } + + function mutate_anchors() { + foreach ($this->content as &$elem) { + if (rand(0,1)) { + $elem['right'] = $this->width - $elem['left'] - (int)(0.5*$elem['font-size']); + unset($elem['left']); + } + if (rand(0,1)) { + $elem['bottom'] = $this->height - $elem['top'] - (int)(1.5*$elem['font-size']); + unset($elem['top']); + } + } + } + + function mutate_containers() { + for ($i = 0; $i <= 80; $i++) { + $new = []; + $new['width'] = rand(0, $this->width*2); + $new['height'] = rand(0, $this->height*2); + $new['top'] = rand(-$this->height * 2, $this->height * 2); + $new['bottom'] = $this->height - ($new['top'] + $new['height']); + $new['left'] = rand(-$this->width * 2, $this->width * 2); + $new['right'] = $this->width - ($new['left'] + $new['width']); + + $new['position'] = 'absolute'; + + $new[0] = []; + + $cnt = rand(0,10); + for ($j = 0; $j < $cnt; $j++) { + $elem = array_pop($this->content); + if (!$elem) break; + + if (isset($elem['top'])) $elem['top'] -= $new['top']; + if (isset($elem['bottom'])) $elem['bottom'] -= $new['bottom']; + if (isset($elem['left'])) $elem['left'] -= $new['left']; + if (isset($elem['right'])) $elem['right'] -= $new['right']; + + $new[0][] = $elem; + } + + if (rand (0,1)) unset($new['top']); + else unset($new['bottom']); + if (rand (0,1)) unset($new['left']); + else unset($new['right']); + + $this->content[] = $new; + + shuffle($this->content); + } + } + + function mutate_margins(&$a = false) { + if ($a === false) { + $a = &$this->content; + } + + foreach ($a as &$v) { + $ary = ['top', 'left', 'bottom', 'right']; + shuffle($ary); + $cnt = rand(0,4); + $ary = array_slice($ary, 0, $cnt); + + foreach ($ary as $prop) { + $margin = rand(-1000, 1000); + + $v['margin-'.$prop] = $margin; + + if (isset($v[$prop])) { + $v[$prop] -= $margin; + } + } + + if (is_array($v[0])) { + $this->mutate_margins($v[0]); + } + } + } + + function mutate_styles(&$a = false) { + if ($a === false) { + $a = &$this->content; + } + + foreach ($a as &$v) { + $content = $v[0]; + unset($v[0]); + $styles = array_splice($v, 0, rand(0, 6)); + $v[0] = $content; + + $id_or_class = rand(0,1); + $param = $id_or_class ? "id" : "class"; + $prefix = $id_or_class ? "#" : "."; + $genname = "zz-".base_convert(rand(1,999999999), 10, 36); + + if ($styles || rand(0,1)) { + $this->style .= $prefix.$genname."{"; + $this->style .= $this->rand_whitespace(); + + foreach ($styles as $k => $val) { + if (is_int($val)) { + $val = "".$val."px"; + } + + $this->style .= "$k:"; + $this->style .= $this->rand_whitespace(); + $this->style .= "$val;"; + $this->style .= $this->rand_whitespace(); + } + $this->style .= "}"; + $this->style .= $this->rand_whitespace(); + } + + $v[$param] = $genname; + + if (is_array($v[0])) { + $this->mutate_styles($v[0]); + } + } + } + + function to_html(&$a = false) { + $inside = true; + + if ($a === false) { + if ($this->style) { + echo ""; + } + + echo "
    "; + $a = &$this->content; + $inside = false; + } + + foreach ($a as &$v) { + $letter = $v[0]; + + unset ($v[0]); + + echo "rand_whitespace(1); + + if (isset ($v['id'])) { + echo "id='$v[id]'"; + echo $this->rand_whitespace(1); + + unset ($v['id']); + } + if (isset ($v['class'])) { + echo "class='$v[class]'"; + echo $this->rand_whitespace(1); + + unset ($v['class']); + } + + echo "style='"; + + foreach ($v as $k => $val) { + if (is_int($val)) { + $val = "".$val."px"; + } + + echo "$k:"; + echo $this->rand_whitespace(); + echo "$val;"; + echo $this->rand_whitespace(); + + } + + echo "'>"; + echo $this->rand_whitespace(); + + if (is_array ($letter)) { + $this->to_html($letter); + } + else { + echo $letter; + } + + echo "
    "; + } + + if (!$inside) { + echo "

    "; + } + + } + + function rand_whitespace($r = 0) { + switch (rand($r,4)) { + case 0: + return ""; + case 1: + return "\n"; + case 2: + return "\t"; + case 3: + return " "; + case 4: + return " "; + } + } + + + + function shuffle_assoc(&$array) { + $keys = array_keys($array); + + shuffle($keys); + + foreach($keys as $key) { + $new[$key] = $array[$key]; + } + + $array = $new; + + return true; + } +} + +//$charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789卐"; + +//(new CzaksCaptcha("hotwheels", 300, 80, $charset))->to_html(); +?> diff --git a/inc/captcha/config.php b/inc/captcha/config.php new file mode 100644 index 00000000..1410f615 --- /dev/null +++ b/inc/captcha/config.php @@ -0,0 +1,16 @@ + 'SET NAMES utf8')); +$pdo = new PDO("mysql:dbname=hokachan;host=localhost", "root", "", array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); + +// Captcha expiration: +$expires_in = 300; // 120 seconds + +// Captcha dimensions: +$width = 250; +$height = 80; + +// Captcha length: +$length = 5; diff --git a/inc/captcha/dbschema.sql b/inc/captcha/dbschema.sql new file mode 100644 index 00000000..504ea10c --- /dev/null +++ b/inc/captcha/dbschema.sql @@ -0,0 +1,9 @@ +SET NAMES utf8; + +CREATE TABLE `captchas` ( + `cookie` VARCHAR(50), + `extra` VARCHAR(200), + `text` VARCHAR(255), + `created_at` INT(11), + PRIMARY KEY (cookie, extra) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; diff --git a/inc/captcha/entrypoint.php b/inc/captcha/entrypoint.php new file mode 100644 index 00000000..3b468a2a --- /dev/null +++ b/inc/captcha/entrypoint.php @@ -0,0 +1,85 @@ +prepare("DELETE FROM `captchas` WHERE `created_at` < ?")->execute([time() - $expires_in]); +} + +switch ($mode) { +// Request: GET entrypoint.php?mode=get&extra=1234567890 +// Response: JSON: cookie => "generatedcookie", captchahtml => "captchahtml", expires_in => 120 +case "get": + if (!isset ($_GET['extra'])) { + die(); + } + + header("Content-type: application/json"); + + $extra = $_GET['extra']; + + require_once("config.php"); + + $text = rand_string($length, $extra); + + $captcha = new CzaksCaptcha($text, $width, $height, $extra); + + $cookie = rand_string(20, "abcdefghijklmnopqrstuvwxyz"); + + ob_start(); + $captcha->to_html(); + $html = ob_get_contents(); + ob_end_clean(); + + $query = $pdo->prepare("INSERT INTO `captchas` (`cookie`, `extra`, `text`, `created_at`) VALUES (?, ?, ?, ?)"); + $query->execute( [$cookie, $extra, $text, time()]); + + echo json_encode(["cookie" => $cookie, "captchahtml" => $html, "expires_in" => $expires_in]); + + break; + +// Request: GET entrypoint.php?mode=check&cookie=generatedcookie&extra=1234567890&text=captcha +// Response: 0 OR 1 +case "check": + if (!isset ($_GET['mode']) + || !isset ($_GET['cookie']) + || !isset ($_GET['extra']) + || !isset ($_GET['text'])) { + die(); + } + + require_once("config.php"); + + cleanup($pdo, $expires_in); + + $query = $pdo->prepare("SELECT * FROM `captchas` WHERE `cookie` = ? AND `extra` = ?"); + $query->execute([$_GET['cookie'], $_GET['extra']]); + + $ary = $query->fetchAll(); + + if (!$ary) { + echo "0"; + } + else { + $query = $pdo->prepare("DELETE FROM `captchas` WHERE `cookie` = ? AND `extra` = ?"); + $query->execute([$_GET['cookie'], $_GET['extra']]); + + if ($ary[0]['text'] !== $_GET['text']) { + echo "0"; + } + else { + echo "1"; + } + } + + break; +} From e1bc4f1da94695c48f0bb04d8506367539405832 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:41:49 +0200 Subject: [PATCH 182/212] Update config.php --- inc/captcha/config.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/inc/captcha/config.php b/inc/captcha/config.php index 1410f615..568c06c5 100644 --- a/inc/captcha/config.php +++ b/inc/captcha/config.php @@ -1,16 +1,16 @@ 'SET NAMES utf8')); -$pdo = new PDO("mysql:dbname=hokachan;host=localhost", "root", "", array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); + +$pdo = new PDO("mysql:dbname=database_name;host=localhost", "database_user", "database_password", array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); + // Captcha expiration: -$expires_in = 300; // 120 seconds +$expires_in = 120; // 120 seconds // Captcha dimensions: $width = 250; $height = 80; // Captcha length: -$length = 5; +$length = 6; From e94f25d4cd4434fbc856b23f856c0e3f8726b2f0 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:42:58 +0200 Subject: [PATCH 183/212] Add files via upload --- js/captcha.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 js/captcha.js diff --git a/js/captcha.js b/js/captcha.js new file mode 100644 index 00000000..018588b7 --- /dev/null +++ b/js/captcha.js @@ -0,0 +1,43 @@ +var tout; + +function redo_events(provider, extra) { + $('.captcha .captcha_text, textarea[id="body"]').off("focus").one("focus", function() { actually_load_captcha(provider, extra); }); +} + +function actually_load_captcha(provider, extra) { + $('.captcha .captcha_text, textarea[id="body"]').off("focus"); + + if (tout !== undefined) { + clearTimeout(tout); + } + + $.getJSON(provider, {mode: 'get', extra: extra}, function(json) { + $(".captcha .captcha_cookie").val(json.cookie); + $(".captcha .captcha_html").html(json.captchahtml); + + setTimeout(function() { + redo_events(provider, extra); + }, json.expires_in * 1000); + }); +} + +function load_captcha(provider, extra) { + $(function() { + $(".captcha>td").html(""+ + ""+ + "
    "); + + $("#quick-reply .captcha .captcha_text").prop("placeholder", _("Verification")); + + $(".captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); }); + $(document).on("ajax_after_post", function() { actually_load_captcha(provider, extra); }); + redo_events(provider, extra); + + $(window).on("quick-reply", function() { + redo_events(provider, extra); + $("#quick-reply .captcha .captcha_html").html($("form:not(#quick-reply) .captcha .captcha_html").html()); + $("#quick-reply .captcha .captcha_cookie").val($("form:not(#quick-reply) .captcha .captcha_cookie").html()); + $("#quick-reply .captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); }); + }); + }); +} \ No newline at end of file From 5dbfc0ab241ddb6d045f7854de38cf8375e369b0 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:44:39 +0200 Subject: [PATCH 184/212] Update post_form.html --- templates/post_form.html | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/templates/post_form.html b/templates/post_form.html index 0e0d3fed..06d3c275 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -79,6 +79,27 @@ {% endif %} + {% if config.captcha.enabled %} + + + {% trans %}Verification{% endtrans %} + + + + + + {% elseif config.new_thread_capt %} + {% if not id %} + + + {% trans %}Verification{% endtrans %} + + + + + + {% endif %} + {% endif %} {% if config.user_flag %} {% trans %}Flag{% endtrans %} From ce755b8b511e84a46643c127627005cff6640d07 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:46:00 +0200 Subject: [PATCH 185/212] Update quick-reply.js --- js/quick-reply.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/quick-reply.js b/js/quick-reply.js index 408de410..cd1b9e8a 100644 --- a/js/quick-reply.js +++ b/js/quick-reply.js @@ -281,7 +281,7 @@ $postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment')); - $postForm.find('textarea:not([name="body"]),input[type="hidden"]').removeAttr('id').appendTo($dummyStuff); + $postForm.find('textarea:not([name="body"]),input[type="hidden"]:not(.captcha_cookie)').removeAttr('id').appendTo($dummyStuff); $postForm.find('br').remove(); $postForm.find('table').prepend('\ From 8b0c86d8bd02bd98128f5b732e081a0d18b40d10 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:49:38 +0200 Subject: [PATCH 186/212] Update install.sql --- install.sql | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/install.sql b/install.sql index 1acc849f..26d21edc 100644 --- a/install.sql +++ b/install.sql @@ -303,7 +303,7 @@ CREATE TABLE IF NOT EXISTS `ban_appeals` ( -- Table structure for table `pages` -- -CREATE TABLE `pages` ( +CREATE TABLE IF NOT EXISTS `pages` ( `id` int(11) NOT NULL AUTO_INCREMENT, `board` varchar(58) CHARACTER SET utf8 DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8 NOT NULL, @@ -320,7 +320,7 @@ CREATE TABLE `pages` ( -- Table structure for table `nntp_references` -- -CREATE TABLE `nntp_references` ( +CREATE TABLE IF NOT EXISTS `nntp_references` ( `board` varchar(30) NOT NULL, `id` int(11) unsigned NOT NULL, `message_id` varchar(255) CHARACTER SET ascii NOT NULL, @@ -332,6 +332,20 @@ CREATE TABLE `nntp_references` ( UNIQUE KEY `u_board_id` (`board`, `id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +-- -------------------------------------------------------- + +-- +-- Table structure for table `captchas` +-- + +CREATE TABLE IF NOT EXISTS `captchas` ( + `cookie` VARCHAR(50), + `extra` VARCHAR(200), + `text` VARCHAR(255), + `created_at` INT(11), + PRIMARY KEY (cookie, extra) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; From 24491cf3c42ee1cbd9c208d6063620ec2cc2193a Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 11:51:26 +0200 Subject: [PATCH 187/212] Update install.php --- install.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/install.php b/install.php index 6f1617a8..ff994010 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); From dc7a507526034fbdeb146fb6749c93c3d06f0a46 Mon Sep 17 00:00:00 2001 From: Horija Date: Mon, 24 Apr 2017 12:21:33 +0200 Subject: [PATCH 188/212] Update post.php --- post.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/post.php b/post.php index 47c5f0ef..fb1ee645 100644 --- a/post.php +++ b/post.php @@ -384,7 +384,20 @@ if (isset($_POST['delete'])) { if (!$resp->is_valid) { error($config['error']['captcha']); } + // Same, but now with our custom captcha provider + if (($config['captcha']['enabled']) || (($post['op']) && ($config['new_thread_capt'])) ) { + $resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([ + 'mode' => 'check', + 'text' => $_POST['captcha_text'], + 'extra' => $config['captcha']['extra'], + 'cookie' => $_POST['captcha_cookie'] + ])); + if ($resp !== '1') { + error($config['error']['captcha'] . + ''); } + } +} if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || (!$post['op'] && $_POST['post'] == $config['button_reply']))) From e0f913a38f9f14e7601692e2d05b4f84df894f20 Mon Sep 17 00:00:00 2001 From: Horija Date: Tue, 25 Apr 2017 05:59:42 +0200 Subject: [PATCH 189/212] forgot Grave accents --- install.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sql b/install.sql index 26d21edc..ca60a58a 100644 --- a/install.sql +++ b/install.sql @@ -343,7 +343,7 @@ CREATE TABLE IF NOT EXISTS `captchas` ( `extra` VARCHAR(200), `text` VARCHAR(255), `created_at` INT(11), - PRIMARY KEY (cookie, extra) + PRIMARY KEY (`cookie`,`extra`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; From bb86d55b1fa4e9844f477914a5e1934ec41264e3 Mon Sep 17 00:00:00 2001 From: Hollick Date: Fri, 28 Apr 2017 16:09:15 +0200 Subject: [PATCH 190/212] Fixed DNSBL TOR Don't know if Sectoor.de comes online again. Found this new dnsbl tor blacklist checker. in case you really need a tor blacklist. --- inc/config.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index 6d1a8081..adff978f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -196,7 +196,11 @@ // Prevents most Tor exit nodes from making posts. Recommended, as a lot of abuse comes from Tor because // of the strong anonymity associated with it. - $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); + // Example: $config['dnsbl'][] = 'another.blacklist.net'; // + // $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); //sectoor.de site is dead. the number stands for (an) ip adress(es) I guess. + + // Replacement for sectoor will be the new one if sectoor stays dead + $config['dnsbl'][] = 'torexit.dan.me.uk'; // http://www.sorbs.net/using.shtml // $config['dnsbl'][] = array('dnsbl.sorbs.net', array(2, 3, 4, 5, 6, 7, 8, 9)); From c4358b078efcb21e8a1cbf3082111651385373d3 Mon Sep 17 00:00:00 2001 From: Hollick Date: Fri, 28 Apr 2017 16:19:32 +0200 Subject: [PATCH 191/212] Update config.php --- inc/config.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/config.php b/inc/config.php index adff978f..b75e564f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -197,10 +197,10 @@ // Prevents most Tor exit nodes from making posts. Recommended, as a lot of abuse comes from Tor because // of the strong anonymity associated with it. // Example: $config['dnsbl'][] = 'another.blacklist.net'; // - // $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); //sectoor.de site is dead. the number stands for (an) ip adress(es) I guess. + $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); //sectoor.de site is dead. the number stands for (an) ip adress(es) I guess. - // Replacement for sectoor will be the new one if sectoor stays dead - $config['dnsbl'][] = 'torexit.dan.me.uk'; + // Replacement for sectoor.de + // $config['dnsbl'][] = 'torexit.dan.me.uk'; // http://www.sorbs.net/using.shtml // $config['dnsbl'][] = array('dnsbl.sorbs.net', array(2, 3, 4, 5, 6, 7, 8, 9)); From 29409f145621fa70f93e7527711285dea6affa11 Mon Sep 17 00:00:00 2001 From: Hollick Date: Fri, 28 Apr 2017 19:07:17 +0200 Subject: [PATCH 192/212] Update config.php Well I'm just putting this info out there for those that need tor protection. no reason to pull this if not stable. --- inc/config.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/config.php b/inc/config.php index b75e564f..9621bb58 100644 --- a/inc/config.php +++ b/inc/config.php @@ -197,10 +197,10 @@ // Prevents most Tor exit nodes from making posts. Recommended, as a lot of abuse comes from Tor because // of the strong anonymity associated with it. // Example: $config['dnsbl'][] = 'another.blacklist.net'; // - $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); //sectoor.de site is dead. the number stands for (an) ip adress(es) I guess. + // $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); //sectoor.de site is dead. the number stands for (an) ip adress(es) I guess. // Replacement for sectoor.de - // $config['dnsbl'][] = 'torexit.dan.me.uk'; + $config['dnsbl'][] = 'torexit.dan.me.uk'; // http://www.sorbs.net/using.shtml // $config['dnsbl'][] = array('dnsbl.sorbs.net', array(2, 3, 4, 5, 6, 7, 8, 9)); From 736e98294516955562495879210a38906750fe97 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 17 May 2017 14:03:48 -0400 Subject: [PATCH 193/212] [SECURITY] Lessen security impact post.php: misc fixes --- mod.php | 8 ++++---- post.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mod.php b/mod.php index 0378d55c..45cbaba1 100644 --- a/mod.php +++ b/mod.php @@ -94,10 +94,10 @@ $pages = array( '/config/(\%b)' => 'secure_POST config', // config editor // these pages aren't listed in the dashboard without $config['debug'] - '/debug/antispam' => 'debug_antispam', - '/debug/recent' => 'debug_recent_posts', - '/debug/apc' => 'debug_apc', - '/debug/sql' => 'secure_POST debug_sql', + //'/debug/antispam' => 'debug_antispam', + //'/debug/recent' => 'debug_recent_posts', + //'/debug/apc' => 'debug_apc', + //'/debug/sql' => 'secure_POST debug_sql', // This should always be at the end: '/(\%b)/' => 'view_board', diff --git a/post.php b/post.php index 2989f910..3d0e618d 100644 --- a/post.php +++ b/post.php @@ -210,7 +210,7 @@ if (isset($_POST['delete'])) { error($config['error']['nodelete']); foreach ($delete as &$id) { - $query = prepare(sprintf("SELECT `thread`, `time`,`password` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); + $query = prepare(sprintf("SELECT `id`,`thread`,`time`,`password` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); @@ -240,7 +240,7 @@ if (isset($_POST['delete'])) { deletePost($id); modLog("User deleted his own post #$id"); } - + _syslog(LOG_INFO, 'Deleted post: ' . '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') ); @@ -318,7 +318,7 @@ if (isset($_POST['delete'])) { $post = $query->fetch(PDO::FETCH_ASSOC); - $error = event('report', array('ip' => $_SERVER['REMOTE_ADDR'], 'board' => $board['uri'], 'post' => $post, 'reason' => $reason)); + $error = event('report', array('ip' => $_SERVER['REMOTE_ADDR'], 'board' => $board['uri'], 'post' => $post, 'reason' => $reason, 'link' => link_for($post))); if ($error) { error($error); From 40fe35fedce79557fdc8b7e5c208d7d279757803 Mon Sep 17 00:00:00 2001 From: czaks Date: Wed, 17 May 2017 14:54:35 -0400 Subject: [PATCH 194/212] early 404 staged --- inc/config.php | 1 + inc/functions.php | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index 6d1a8081..db424e36 100644 --- a/inc/config.php +++ b/inc/config.php @@ -524,6 +524,7 @@ $config['early_404_page'] = 3; $config['early_404_replies'] = 5; + $config['early_404_staged'] = false; // A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts // as they are submitted and changes or censors particular words or phrases. diff --git a/inc/functions.php b/inc/functions.php index 1b336e12..c87a6c3c 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1311,11 +1311,28 @@ function clean($pid = false) { $query->bindValue(':offset', $offset, PDO::PARAM_INT); $query->execute() or error(db_error($query)); + if ($config['early_404_staged']) { + $page = $config['early_404_page']; + $iter = 0; + } + else { + $page = 1; + } + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - if ($post['reply_count'] < $config['early_404_replies']) { + if ($post['reply_count'] < $page*$config['early_404_replies']) { deletePost($post['thread_id'], false, false); if ($pid) modLog("Automatically deleting thread #{$post['thread_id']} due to new thread #{$pid} (early 404 is set, #{$post['thread_id']} had {$post['reply_count']} replies)"); } + + if ($config['early_404_staged']) { + $iter++; + + if ($iter == $config['threads_per_page']) { + $page++; + $iter = 0; + } + } } } } From 4f85b7c57054e6af8eee4c2d9fbb5cf08c1e7aea Mon Sep 17 00:00:00 2001 From: antedeguemon Date: Sun, 21 May 2017 17:08:43 -0300 Subject: [PATCH 195/212] Fixed XSS in post edit page and modsearch --- templates/mod/edit_post_form.html | 4 ++-- templates/mod/search_results.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/mod/edit_post_form.html b/templates/mod/edit_post_form.html index a2980d35..7f8d4b1e 100644 --- a/templates/mod/edit_post_form.html +++ b/templates/mod/edit_post_form.html @@ -7,7 +7,7 @@ {% trans %}Name{% endtrans %} - + @@ -23,7 +23,7 @@ {% trans %}Subject{% endtrans %} - + diff --git a/templates/mod/search_results.html b/templates/mod/search_results.html index 186ee5db..abaad703 100644 --- a/templates/mod/search_results.html +++ b/templates/mod/search_results.html @@ -224,7 +224,7 @@