summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCzterooki <czterooki1337@gmail.com>2013-07-20 00:19:08 -0400
committerCzterooki <czterooki1337@gmail.com>2013-07-20 00:19:08 -0400
commitaef44255920c18f443af28d100fb06d9699d4895 (patch)
treeb1d72ffc9a0e4f6a009ca16e9e3af2260806c193
parent81892ebcdcd2929128c2c320f7de975cab8ed368 (diff)
parentf04566f6d30553560d5a69e25e38de72a60f297c (diff)
merge with vichan-gold
-rw-r--r--.gitignore2
-rw-r--r--README.md7
-rw-r--r--inc/api.php123
-rw-r--r--inc/config.php27
-rw-r--r--inc/display.php23
-rw-r--r--inc/filters.php2
-rw-r--r--inc/functions.php147
-rw-r--r--inc/lib/Twig/Extensions/Extension/Tinyboard.php6
-rw-r--r--inc/locale/pl_PL/LC_MESSAGES/javascript.js1
-rw-r--r--inc/locale/pl_PL/LC_MESSAGES/javascript.po121
-rw-r--r--inc/locale/pl_PL/LC_MESSAGES/tinyboard.mobin13500 -> 14191 bytes
-rw-r--r--inc/locale/pl_PL/LC_MESSAGES/tinyboard.po520
-rw-r--r--inc/locale/pt_BR/LC_MESSAGES/tinyboard.mobin0 -> 12327 bytes
-rw-r--r--inc/locale/pt_BR/LC_MESSAGES/tinyboard.po844
-rw-r--r--inc/mod/auth.php9
-rw-r--r--inc/mod/ban.php17
-rw-r--r--inc/mod/pages.php100
-rw-r--r--install.php5
-rw-r--r--install.sql1
-rw-r--r--js/auto-reload.js3
-rw-r--r--js/expand.js7
-rw-r--r--js/forced-anon.js6
-rw-r--r--js/inline-expanding.js60
-rw-r--r--js/local-time.js36
-rw-r--r--js/post-hider.js62
-rw-r--r--js/post-hover.js2
-rw-r--r--js/quick-post-controls.js12
-rw-r--r--js/quick-reply.js11
-rw-r--r--js/show-backlinks.js4
-rw-r--r--js/smartphone-spoiler.js21
-rw-r--r--js/toggle-locked-threads.js4
-rw-r--r--mod.php10
-rw-r--r--post.php34
-rw-r--r--stylesheets/gentoochan.css67
-rw-r--r--templates/banned.html16
-rw-r--r--templates/boardlist.html1
-rw-r--r--templates/index.html22
-rw-r--r--templates/main.js24
-rw-r--r--templates/mod/ban_list.html8
-rw-r--r--templates/mod/dashboard.html2
-rw-r--r--templates/mod/view_ip.html50
-rw-r--r--templates/page.html2
-rwxr-xr-xtemplates/post_form.html21
-rw-r--r--templates/post_reply.html8
-rw-r--r--templates/post_thread.html300
-rw-r--r--templates/themes/basic/theme.php2
-rw-r--r--templates/themes/catalog/catalog.css38
-rw-r--r--templates/themes/catalog/catalog.html36
-rw-r--r--templates/themes/catalog/info.php42
-rw-r--r--templates/themes/catalog/theme.php60
-rw-r--r--templates/themes/categories/theme.php2
-rw-r--r--templates/themes/frameset/theme.php2
-rw-r--r--templates/themes/recent/theme.php5
-rw-r--r--templates/themes/rrdtool/theme.php2
-rw-r--r--templates/themes/sitemap/info.php53
-rw-r--r--templates/themes/sitemap/sitemap.xml19
-rw-r--r--templates/themes/sitemap/theme.php32
-rw-r--r--templates/themes/ukko/info.php53
-rw-r--r--templates/themes/ukko/theme.php111
-rw-r--r--templates/themes/ukko/thumb.pngbin0 -> 15123 bytes
-rw-r--r--templates/themes/ukko/ukko.js37
-rw-r--r--templates/thread.html36
-rw-r--r--tools/benchmark.php52
-rwxr-xr-xtools/i18n_compile.php44
-rwxr-xr-xtools/i18n_extract.php47
-rw-r--r--tools/inc/cli.php100
-rw-r--r--tools/inc/lib/jsgettext/JSParser.php94
-rw-r--r--tools/inc/lib/jsgettext/PoeditParser.php83
-rw-r--r--tools/inc/lib/jsgettext/PoeditString.php29
-rw-r--r--tools/inc/lib/jsgettext/jsgettext.php63
-rw-r--r--tools/inc/lib/jsgettext/po2json.php42
-rwxr-xr-xtools/rebuild.php105
72 files changed, 3508 insertions, 429 deletions
diff --git a/.gitignore b/.gitignore
index 7657407c..eee0e4e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,7 @@
/templates/cache
# other stuff
-.DS_Store?
+.DS_Store
thumbs.db
Icon?
Thumbs.db
diff --git a/README.md b/README.md
index 50dcc621..b98e0dad 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,13 @@
Tinyboard - A lightweight PHP imageboard.
==========================================
+Tinyboard + vichan-devel
+------------
+Tinyboard branch taking lightweightness somewhat more liberally. Running live at
+https://pl.vichan.net/ (Polish) and http://vichan.net/ (International; may be outdated).
+
+It contains many changes from original Tinyboard, mainly in frontend area.
+
About
------------
Tinyboard is a light-weight, fast, highly configurable and user-friendly
diff --git a/inc/api.php b/inc/api.php
new file mode 100644
index 00000000..5592d8af
--- /dev/null
+++ b/inc/api.php
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * Copyright (c) 2010-2013 Tinyboard Development Group
+ */
+
+/**
+ * Class for generating json API compatible with 4chan API
+ */
+class Api {
+
+ /**
+ * Translation from local fields to fields in 4chan-style API
+ */
+ public static $postFields = array(
+ 'id' => 'no',
+ 'thread' => 'resto',
+ 'subject' => 'sub',
+ 'email' => 'email',
+ 'name' => 'name',
+ 'trip' => 'trip',
+ 'capcode' => 'capcode',
+ 'body' => 'com',
+ 'time' => 'time',
+ 'thumb' => 'thumb', // non-compatible field
+ 'thumbx' => 'tn_w',
+ 'thumby' => 'tn_h',
+ 'file' => 'file', // non-compatible field
+ 'filex' => 'w',
+ 'filey' => 'h',
+ 'filesize' => 'fsize',
+ //'filename' => 'filename',
+ 'omitted' => 'omitted_posts',
+ 'omitted_images' => 'omitted_images',
+ //'posts' => 'replies',
+ //'ip' => '',
+ 'sticky' => 'sticky',
+ 'locked' => 'locked',
+ //'bumplocked' => '',
+ //'embed' => '',
+ //'root' => '',
+ //'mod' => '',
+ //'hr' => '',
+ );
+
+ static $ints = array(
+ 'no' => 1,
+ 'resto' => 1,
+ 'time' => 1,
+ 'tn_w' => 1,
+ 'tn_h' => 1,
+ 'w' => 1,
+ 'h' => 1,
+ 'fsize' => 1,
+ 'omitted_posts' => 1,
+ 'omitted_images' => 1,
+ 'sticky' => 1,
+ 'locked' => 1,
+ );
+
+ private function translatePost($post) {
+ $apiPost = array();
+ foreach (self::$postFields as $local => $translated) {
+ if (!isset($post->$local))
+ continue;
+
+ $toInt = isset(self::$ints[$translated]);
+ $val = $post->$local;
+ if ($val !== null && $val !== '') {
+ $apiPost[$translated] = $toInt ? (int) $val : $val;
+ }
+ }
+
+ if (isset($post->filename)) {
+ $dotPos = strrpos($post->filename, '.');
+ $apiPost['filename'] = substr($post->filename, 0, $dotPos);
+ $apiPost['ext'] = substr($post->filename, $dotPos);
+ }
+
+ return $apiPost;
+ }
+
+ function translateThread(Thread $thread) {
+ $apiPosts = array();
+ $op = $this->translatePost($thread);
+ $op['resto'] = 0;
+ $apiPosts['posts'][] = $op;
+
+ foreach ($thread->posts as $p) {
+ $apiPosts['posts'][] = $this->translatePost($p);
+ }
+
+ return $apiPosts;
+ }
+
+ function translatePage(array $threads) {
+ $apiPage = array();
+ foreach ($threads as $thread) {
+ $apiPage['threads'][] = $this->translateThread($thread);
+ }
+ return $apiPage;
+ }
+
+ function translateCatalogPage(array $threads) {
+ $apiPage = array();
+ foreach ($threads as $thread) {
+ $ts = $this->translateThread($thread);
+ $apiPage['threads'][] = current($ts['posts']);
+ }
+ return $apiPage;
+ }
+
+ function translateCatalog($catalog) {
+ $apiCatalog = array();
+ foreach ($catalog as $page => $threads) {
+ $apiPage = $this->translateCatalogPage($threads);
+ $apiPage['page'] = $page;
+ $apiCatalog[] = $apiPage;
+ }
+
+ return $apiCatalog;
+ }
+}
diff --git a/inc/config.php b/inc/config.php
index 729c23ea..a3b3d528 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -326,6 +326,11 @@
// Reply limit (stops bumping thread when this is reached)
$config['reply_limit'] = 250;
+ // Image hard limit (stops allowing new image replies when this is reached if not zero)
+ $config['image_hard_limit'] = 0;
+ // Reply hard limit (stops allowing new replies when this is reached if not zero)
+ $config['reply_hard_limit'] = 0;
+
// Strip repeating characters when making hashes
$config['robot_enable'] = false;
$config['robot_strip_repeating'] = true;
@@ -379,6 +384,9 @@
// When true, a blank password will be used for files (not usable for deletion).
$config['field_disable_password'] = false;
+ // Require users to see the ban page at least once for a ban even if it has since expired?
+ $config['require_ban_view'] = false;
+
/*
* ====================
* Markup settings
@@ -552,6 +560,9 @@
// Number of characters in the poster ID (maximum is 40)
$config['poster_id_length'] = 5;
+ // Show thread subject in page title?
+ $config['thread_subject_in_title'] = false;
+
// Page footer
$config['footer'][] = 'All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.';
@@ -696,6 +707,8 @@
$config['error']['noboard'] = _('Invalid board!');
$config['error']['nonexistant'] = _('Thread specified does not exist.');
$config['error']['locked'] = _('Thread locked. You may not reply at this time.');
+ $config['error']['reply_hard_limit'] = _('Thread has reached its maximum reply limit.');
+ $config['error']['image_hard_limit'] = _('Thread has reached its maximum image limit.');
$config['error']['nopost'] = _('You didn\'t make a post.');
$config['error']['flood'] = _('Flood detected; Post discarded.');
$config['error']['spam'] = _('Your request looks automated; Post discarded.');
@@ -723,6 +736,7 @@
$config['error']['captcha'] = _('You seem to have mistyped the verification.');
// Moderator errors
+ $config['error']['toomanyunban'] = _('You are only allowed to unban %s users at a time. You tried to unban %u users.');
$config['error']['invalid'] = _('Invalid username and/or password.');
$config['error']['notamod'] = _('You are not a mod…');
$config['error']['invalidafter'] = _('Invalid username and/or password. Your user may have been deleted or changed.');
@@ -810,6 +824,9 @@
* Mod settings
* ====================
*/
+
+ // Limit how many bans can be removed via the ban list. (Set too -1 to remove limit.)
+ $config['mod']['unban_limit'] = 5;
// Whether or not to lock moderator sessions to the IP address that was logged in with.
$config['mod']['lock_ip'] = true;
@@ -900,8 +917,8 @@
$config['mod']['shadow_mesage'] = 'Moved to %s.';
// Capcode to use when posting the above message.
$config['mod']['shadow_capcode'] = 'Mod';
- // Name to use when posting the above message.
- $config['mod']['shadow_name'] = $config['anonymous'];
+ // Name to use when posting the above message. If false, the default board name will be used. If something else, that will be used.
+ $config['mod']['shadow_name'] = false;
// Wait indefinitely when rebuilding everything
$config['mod']['rebuild_timelimit'] = 0;
@@ -912,6 +929,9 @@
// Edit raw HTML in posts by default
$config['mod']['raw_html_default'] = false;
+ // Automatically dismiss all reports regarding a thread when it is locked
+ $config['mod']['dismiss_reports_on_lock'] = true;
+
// Probably best not to change these:
if (!defined('JANITOR')) {
define('JANITOR', 0, true);
@@ -1028,6 +1048,9 @@
$config['mod']['createusers'] = ADMIN;
// View the moderation log
$config['mod']['modlog'] = ADMIN;
+ // View relevant moderation log entries on IP address pages (ie. ban history, etc.)
+ // Warning: Can be pretty resource exhaustive if your mod logs are huge.
+ $config['mod']['modlog_ip'] = MOD;
// Create a PM (viewing mod usernames)
$config['mod']['create_pm'] = JANITOR;
// Read any PM, sent to or from anybody
diff --git a/inc/display.php b/inc/display.php
index 9be772e4..ba55903d 100644
--- a/inc/display.php
+++ b/inc/display.php
@@ -118,7 +118,7 @@ function pm_snippet($body, $len=null) {
// calculate strlen() so we can add "..." after if needed
$strlen = mb_strlen($body);
- $body = substr($body, 0, $len);
+ $body = mb_substr($body, 0, $len);
// Re-escape the characters.
return '<em>' . utf8tohtml($body) . ($strlen > $len ? '&hellip;' : '') . '</em>';
@@ -204,7 +204,7 @@ function truncate($body, $url, $max_lines = false, $max_chars = false) {
}
} else {
// remove broken HTML entity at the end (if existent)
- $body = preg_replace('/&[^;]+$/', '', $body);
+ $body = preg_replace('/&[^;]*$/', '', $body);
}
$body .= '<span class="toolong">Post too long. Click <a href="' . $url . '">here</a> to view the full text.</span>';
@@ -213,6 +213,25 @@ function truncate($body, $url, $max_lines = false, $max_chars = false) {
return $body;
}
+function bidi_cleanup($str){
+ # Removes all embedded RTL and LTR unicode formatting blocks in a string so that
+ # it can be used inside another without controlling its direction.
+ # More info: http://www.iamcal.com/understanding-bidirectional-text/
+ #
+ # LRE - U+202A - 0xE2 0x80 0xAA
+ # RLE - U+202B - 0xE2 0x80 0xAB
+ # LRO - U+202D - 0xE2 0x80 0xAD
+ # RLO - U+202E - 0xE2 0x80 0xAE
+ #
+ # PDF - U+202C - 0xE2 0x80 0xAC
+ #
+ $explicits = '\xE2\x80\xAA|\xE2\x80\xAB|\xE2\x80\xAD|\xE2\x80\xAE';
+ $pdf = '\xE2\x80\xAC';
+
+ $str = preg_replace("!(?<explicits>$explicits)|(?<pdf>$pdf)!", '', $str);
+ return $str;
+}
+
function secure_link_confirm($text, $title, $confirm_message, $href) {
global $config;
diff --git a/inc/filters.php b/inc/filters.php
index 0b5eb3c1..524d37cf 100644
--- a/inc/filters.php
+++ b/inc/filters.php
@@ -81,7 +81,7 @@ class Filter {
else
$all_boards = false;
- $query = prepare("INSERT INTO `bans` VALUES (NULL, :ip, :mod, :set, :expires, :reason, :board)");
+ $query = prepare("INSERT INTO `bans` VALUES (NULL, :ip, :mod, :set, :expires, :reason, :board, 0)");
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
$query->bindValue(':mod', -1);
$query->bindValue(':set', time());
diff --git a/inc/functions.php b/inc/functions.php
index 5cf539ce..fe1e254c 100644
--- a/inc/functions.php
+++ b/inc/functions.php
@@ -13,6 +13,7 @@ require_once 'inc/display.php';
require_once 'inc/template.php';
require_once 'inc/database.php';
require_once 'inc/events.php';
+require_once 'inc/api.php';
require_once 'inc/lib/gettext/gettext.inc';
// the user is not currently logged in as a moderator
@@ -78,7 +79,7 @@ function loadConfig() {
if ($config['debug']) {
if (!isset($debug)) {
- $debug = array('sql' => array(), 'purge' => array(), 'cached' => array());
+ $debug = array('sql' => array(), 'purge' => array(), 'cached' => array(), 'write' => array());
$debug['start'] = microtime(true);
}
}
@@ -239,12 +240,12 @@ function create_antibot($board, $thread = null) {
return _create_antibot($board, $thread);
}
-function rebuildThemes($action) {
+function rebuildThemes($action, $board = false) {
// List themes
$query = query("SELECT `theme` FROM `theme_settings` WHERE `name` IS NULL AND `value` IS NULL") or error(db_error());
while ($theme = $query->fetch()) {
- rebuildTheme($theme['theme'], $action);
+ rebuildTheme($theme['theme'], $action, $board);
}
}
@@ -261,7 +262,7 @@ function loadThemeConfig($_theme) {
return $theme;
}
-function rebuildTheme($theme, $action) {
+function rebuildTheme($theme, $action, $board = false) {
global $config, $_theme;
$_theme = $theme;
@@ -270,7 +271,7 @@ function rebuildTheme($theme, $action) {
if (file_exists($config['dir']['themes'] . '/' . $_theme . '/theme.php')) {
require_once $config['dir']['themes'] . '/' . $_theme . '/theme.php';
- $theme['build_function']($action, themeSettings($_theme));
+ $theme['build_function']($action, themeSettings($_theme), $board);
}
}
@@ -328,11 +329,19 @@ function setupBoard($array) {
}
function openBoard($uri) {
+ $board = getBoardInfo($uri);
+ if ($board) {
+ setupBoard($board);
+ return true;
+ }
+ return false;
+}
+
+function getBoardInfo($uri) {
global $config;
if ($config['cache']['enabled'] && ($board = cache::get('board_' . $uri))) {
- setupBoard($board);
- return true;
+ return $board;
}
$query = prepare("SELECT * FROM `boards` WHERE `uri` = :uri LIMIT 1");
@@ -342,27 +351,16 @@ function openBoard($uri) {
if ($board = $query->fetch()) {
if ($config['cache']['enabled'])
cache::set('board_' . $uri, $board);
- setupBoard($board);
- return true;
+ return $board;
}
return false;
}
function boardTitle($uri) {
- global $config;
- if ($config['cache']['enabled'] && ($board = cache::get('board_' . $uri))) {
+ $board = getBoardInfo($uri);
+ if ($board)
return $board['title'];
- }
-
- $query = prepare("SELECT `title` FROM `boards` WHERE `uri` = :uri LIMIT 1");
- $query->bindValue(':uri', $uri);
- $query->execute() or error(db_error($query));
-
- if ($title = $query->fetch()) {
- return $title['title'];
- }
-
return false;
}
@@ -395,7 +393,7 @@ function purge($uri) {
}
function file_write($path, $data, $simple = false, $skip_purge = false) {
- global $config;
+ global $config, $debug;
if (preg_match('/^remote:\/\/(.+)\:(.+)$/', $path, $m)) {
if (isset($config['remote'][$m[1]])) {
@@ -422,7 +420,7 @@ function file_write($path, $data, $simple = false, $skip_purge = false) {
error('Unable to truncate file: ' . $path);
// Write data
- if (fwrite($fp, $data) === false)
+ if (($bytes = fwrite($fp, $data)) === false)
error('Unable to write to file: ' . $path);
// Unlock
@@ -448,6 +446,10 @@ function file_write($path, $data, $simple = false, $skip_purge = false) {
purge($path);
}
+ if ($config['debug']) {
+ $debug['write'][] = $path . ': ' . $bytes . ' bytes';
+ }
+
event('write', $path);
}
@@ -578,6 +580,12 @@ function ago($timestamp) {
function displayBan($ban) {
global $config;
+ if (!$ban['seen']) {
+ $query = prepare("UPDATE `bans` SET `seen` = 1 WHERE `id` = :id");
+ $query->bindValue(':id', $ban['id'], PDO::PARAM_INT);
+ $query->execute() or error(db_error($query));
+ }
+
$ban['ip'] = $_SERVER['REMOTE_ADDR'];
// Show banned page and exit
@@ -604,12 +612,12 @@ function checkBan($board = 0) {
if (event('check-ban', $board))
return true;
- $query = prepare("SELECT `set`, `expires`, `reason`, `board`, `bans`.`id` FROM `bans` WHERE (`board` IS NULL OR `board` = :board) AND `ip` = :ip ORDER BY `expires` IS NULL DESC, `expires` DESC, `expires` DESC LIMIT 1");
+ $query = prepare("SELECT `set`, `expires`, `reason`, `board`, `seen`, `bans`.`id` FROM `bans` WHERE (`board` IS NULL OR `board` = :board) AND `ip` = :ip ORDER BY `expires` IS NULL DESC, `expires` DESC, `expires` DESC LIMIT 1");
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
$query->bindValue(':board', $board);
$query->execute() or error(db_error($query));
if ($query->rowCount() < 1 && $config['ban_range']) {
- $query = prepare("SELECT `set`, `expires`, `reason`, `board`, `bans`.`id` FROM `bans` WHERE (`board` IS NULL OR `board` = :board) AND :ip LIKE REPLACE(REPLACE(`ip`, '%', '!%'), '*', '%') ESCAPE '!' ORDER BY `expires` IS NULL DESC, `expires` DESC LIMIT 1");
+ $query = prepare("SELECT `set`, `expires`, `reason`, `board`, `seen`, `bans`.`id` FROM `bans` WHERE (`board` IS NULL OR `board` = :board) AND :ip LIKE REPLACE(REPLACE(`ip`, '%', '!%'), '*', '%') ESCAPE '!' ORDER BY `expires` IS NULL DESC, `expires` DESC LIMIT 1");
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
$query->bindValue(':board', $board);
$query->execute() or error(db_error($query));
@@ -617,7 +625,7 @@ function checkBan($board = 0) {
if ($query->rowCount() < 1 && $config['ban_cidr'] && !isIPv6()) {
// my most insane SQL query yet
- $query = prepare("SELECT `set`, `expires`, `reason`, `board`, `bans`.`id` FROM `bans` WHERE (`board` IS NULL OR `board` = :board)
+ $query = prepare("SELECT `set`, `expires`, `reason`, `board`, `seen`, `bans`.`id` FROM `bans` WHERE (`board` IS NULL OR `board` = :board)
AND (
`ip` REGEXP '^(\[0-9]+\.\[0-9]+\.\[0-9]+\.\[0-9]+\)\/(\[0-9]+)$'
AND
@@ -634,15 +642,29 @@ function checkBan($board = 0) {
if ($ban = $query->fetch()) {
if ($ban['expires'] && $ban['expires'] < time()) {
// Ban expired
- $query = prepare("DELETE FROM `bans` WHERE `id` = :id LIMIT 1");
+ $query = prepare("DELETE FROM `bans` WHERE `id` = :id");
$query->bindValue(':id', $ban['id'], PDO::PARAM_INT);
$query->execute() or error(db_error($query));
+ if ($config['require_ban_view'] && !$ban['seen']) {
+ displayBan($ban);
+ }
+
return;
}
displayBan($ban);
}
+
+ // I'm not sure where else to put this. It doesn't really matter where; it just needs to be called every now and then to keep the ban list tidy.
+ purge_bans();
+}
+
+// No reason to keep expired bans in the database (except those that haven't been viewed yet)
+function purge_bans() {
+ $query = prepare("DELETE FROM `bans` WHERE `expires` IS NOT NULL AND `expires` < :time AND `seen` = 1");
+ $query->bindValue(':time', time());
+ $query->execute() or error(db_error($query));
}
function threadLocked($id) {
@@ -725,13 +747,13 @@ function post(array $post) {
$query->bindValue(':password', $post['password']);
$query->bindValue(':ip', isset($post['ip']) ? $post['ip'] : $_SERVER['REMOTE_ADDR']);
- if ($post['op'] && $post['mod'] && $post['sticky']) {
+ if ($post['op'] && $post['mod'] && isset($post['sticky']) && $post['sticky']) {
$query->bindValue(':sticky', 1, PDO::PARAM_INT);
} else {
$query->bindValue(':sticky', 0, PDO::PARAM_INT);
}
- if ($post['op'] && $post['mod'] && $post['locked']) {
+ if ($post['op'] && $post['mod'] && isset($post['locked']) && $post['locked']) {
$query->bindValue(':locked', 1, PDO::PARAM_INT);
} else {
$query->bindValue(':locked', 0, PDO::PARAM_INT);
@@ -967,6 +989,8 @@ function index($page, $mod=false) {
if ($query->rowcount() < 1 && $page > 1)
return false;
+
+ $threads = array();
while ($th = $query->fetch()) {
$thread = new Thread(
$th['id'], $th['subject'], $th['email'], $th['name'], $th['trip'], $th['capcode'], $th['body'], $th['time'], $th['thumb'],
@@ -986,12 +1010,8 @@ function index($page, $mod=false) {
$replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC));
if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) {
- $count = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM `posts_%s` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM `posts_%s` WHERE `file` IS NOT NULL AND `thread` = :thread", $board['uri'], $board['uri']));
- $count->bindValue(':thread', $th['id'], PDO::PARAM_INT);
- $count->execute() or error(db_error($count));
- $count = $count->fetchAll(PDO::FETCH_COLUMN);
-
- $omitted = array('post_count' => $count[0], 'image_count' => $count[1]);
+ $count = numPosts($th['id']);
+ $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']);
} else {
$omitted = false;
}
@@ -1019,7 +1039,8 @@ function index($page, $mod=false) {
$thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']);
$thread->omitted_images = $omitted['image_count'] - $num_images;
}
-
+
+ $threads[] = $thread;
$body .= $thread->build(true);
}
@@ -1028,7 +1049,8 @@ function index($page, $mod=false) {
'body' => $body,
'post_url' => $config['post_url'],
'config' => $config,
- 'boardlist' => createBoardlist($mod)
+ 'boardlist' => createBoardlist($mod),
+ 'threads' => $threads
);
}
@@ -1134,14 +1156,19 @@ function checkRobot($body) {
return false;
}
+// Returns an associative array with 'replies' and 'images' keys
function numPosts($id) {
global $board;
- $query = prepare(sprintf("SELECT COUNT(*) as `count` FROM `posts_%s` WHERE `thread` = :thread", $board['uri']));
+ $query = prepare(sprintf("SELECT COUNT(*) as `num` FROM `posts_%s` WHERE `thread` = :thread UNION ALL SELECT COUNT(*) FROM `posts_%s` WHERE `file` IS NOT NULL AND `thread` = :thread", $board['uri'], $board['uri']));
$query->bindValue(':thread', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
- $result = $query->fetch();
- return $result['count'];
+ $num_posts = $query->fetch();
+ $num_posts = $num_posts['num'];
+ $num_images = $query->fetch();
+ $num_images = $num_images['num'];
+
+ return array('replies' => $num_posts, 'images' => $num_images);
}
function muteTime() {
@@ -1213,6 +1240,9 @@ function buildIndex() {
$pages = getPages();
$antibot = create_antibot($board['uri']);
+ $api = new Api();
+ $catalog = array();
+
$page = 1;
while ($page <= $config['max_pages'] && $content = index($page)) {
$filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page));
@@ -1225,6 +1255,14 @@ function buildIndex() {
$content['antibot'] = $antibot;
file_write($filename, Element('index.html', $content));
+
+ // json api
+ $threads = $content['threads'];
+ $json = json_encode($api->translatePage($threads));
+ $jsonFilename = $board['dir'] . ($page-1) . ".json"; // pages should start from 0
+ file_write($jsonFilename, $json);
+
+ $catalog[$page-1] = $threads;
$page++;
}
@@ -1232,8 +1270,16 @@ function buildIndex() {
for (;$page<=$config['max_pages'];$page++) {
$filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page));
file_unlink($filename);
+
+ $jsonFilename = $board['dir'] . ($page-1) . ".json";
+ file_unlink($jsonFilename);
}
}
+
+ // json api catalog
+ $json = json_encode($api->translateCatalog($catalog));
+ $jsonFilename = $board['dir'] . "catalog.json";
+ file_write($jsonFilename, $json);
}
function buildJavascript() {
@@ -1246,6 +1292,12 @@ function buildJavascript() {
'uri' => addslashes((!empty($uri) ? $config['uri_stylesheets'] : '') . $uri));
}
+ // Check if we have translation for the javascripts; if yes, we add it to additional javascripts
+ list($pure_locale) = explode(".", $config['locale']);
+ if (file_exists ($jsloc = "inc/locale/".$pure_locale."/LC_MESSAGES/javascript.js")) {
+ array_unshift($config['additional_javascript'], $jsloc);
+ }
+
$script = Element('main.js', array(
'config' => $config,
'stylesheets' => $stylesheets
@@ -1365,8 +1417,8 @@ function unicodify($body) {
// En and em- dashes are rendered exactly the same in
// most monospace fonts (they look the same in code
// editors).
- $body = str_replace('--', '&ndash;', $body); // en dash
$body = str_replace('---', '&mdash;', $body); // em dash
+ $body = str_replace('--', '&ndash;', $body); // en dash
return $body;
}
@@ -1494,7 +1546,7 @@ function markup(&$body, $track_cites = false) {
}
function utf8tohtml($utf8) {
- return mb_encode_numericentity(htmlspecialchars($utf8, ENT_NOQUOTES, 'UTF-8'), array(0x80, 0xffff, 0, 0xffff), 'UTF-8');
+ return htmlspecialchars($utf8, ENT_NOQUOTES, 'UTF-8');
}
function buildThread($id, $return=false, $mod=false) {
@@ -1535,8 +1587,9 @@ function buildThread($id, $return=false, $mod=false) {
error($config['error']['nonexistant']);
$body = Element('thread.html', array(
- 'board'=>$board,
- 'body'=>$thread->build(),
+ 'board' => $board,
+ 'thread' => $thread,
+ 'body' => $thread->build(),
'config' => $config,
'id' => $id,
'mod' => $mod,
@@ -1549,6 +1602,12 @@ function buildThread($id, $return=false, $mod=false) {
return $body;
file_write($board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $id), $body);
+
+ // json api
+ $api = new Api();
+ $json = json_encode($api->translateThread($thread));
+ $jsonFilename = $board['dir'] . $config['dir']['res'] . $id . ".json";
+ file_write($jsonFilename, $json);
}
function rrmdir($dir) {
diff --git a/inc/lib/Twig/Extensions/Extension/Tinyboard.php b/inc/lib/Twig/Extensions/Extension/Tinyboard.php
index 0a128e7b..61cf9fc9 100644
--- a/inc/lib/Twig/Extensions/Extension/Tinyboard.php
+++ b/inc/lib/Twig/Extensions/Extension/Tinyboard.php
@@ -25,6 +25,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
'until' => new Twig_Filter_Function('until'),
'split' => new Twig_Filter_Function('twig_split_filter'),
'push' => new Twig_Filter_Function('twig_push_filter'),
+ 'bidi_cleanup' => new Twig_Filter_Function('bidi_cleanup'),
'addslashes' => new Twig_Filter_Function('addslashes')
);
}
@@ -57,8 +58,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
}
function twig_timezone_function() {
- // there's probably a much easier way of doing this
- return sprintf("%s%02d", ($hr = (int)floor(($tz = date('Z')) / 3600)) > 0 ? '+' : '-', abs($hr)) . ':' . sprintf("%02d", (($tz / 3600) - $hr) * 60);
+ return 'Z';
}
function twig_split_filter($str, $delim) {
@@ -75,7 +75,7 @@ function twig_remove_whitespace_filter($data) {
}
function twig_date_filter($date, $format) {
- return strftime($format, $date);
+ return gmstrftime($format, $date);
}
function twig_hasPermission_filter($mod, $permission, $board = null) {
diff --git a/inc/locale/pl_PL/LC_MESSAGES/javascript.js b/inc/locale/pl_PL/LC_MESSAGES/javascript.js
new file mode 100644
index 00000000..44857b98
--- /dev/null
+++ b/inc/locale/pl_PL/LC_MESSAGES/javascript.js
@@ -0,0 +1 @@
+l10n = {"Submit":"Wy\u015blij","Quick reply":"Szybka odpowied\u017a","Posting mode: Replying to <small>&gt;&gt;{0}<\/small>":"Tryb postowania: Odpowied\u017a na <small>&gt;&gt;{0}<\/small>","Return":"Powr\u00f3t","Click reply to view.":"Kliknij Odpowied\u017a aby zobaczy\u0107.","Click to expand":"Kliknij aby rozwin\u0105\u0107","Hide expanded replies":"Schowaj rozwini\u0119te odpowiedzi","Mon":"pon","Tue":"wto","Wed":"\u015bro","Thu":"czw","Fri":"pi\u0105","Sat":"sob","Sun":"nie","Show locked threads":"Poka\u017c zablokowane tematy","Hide locked threads":"Schowaj zablokowane tematy","Forced anonymity":"Wymuszona anonimowo\u015b\u0107","enabled":"w\u0142\u0105czona","disabled":"wy\u0142\u0105czona","Password":"Has\u0142o","Delete file only":"Usu\u0144 tylko plik","File":"Plik","Delete":"Usu\u0144","Reason":"Pow\u00f3d","Report":"Zg\u0142oszenie"}; \ No newline at end of file
diff --git a/inc/locale/pl_PL/LC_MESSAGES/javascript.po b/inc/locale/pl_PL/LC_MESSAGES/javascript.po
new file mode 100644
index 00000000..3c16c8d7
--- /dev/null
+++ b/inc/locale/pl_PL/LC_MESSAGES/javascript.po
@@ -0,0 +1,121 @@
+# 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 <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-18 16:31-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../../../../js/quick-reply.js:20 ../../../../js/quick-reply.js:21
+msgid "Submit"
+msgstr "Wyślij"
+
+#: ../../../../js/quick-reply.js:30 ../../../../js/quick-reply.js:31
+msgid "Quick reply"
+msgstr "Szybka odpowiedź"
+
+#: ../../../../js/quick-reply.js:32 ../../../../js/quick-reply.js:33
+msgid "Posting mode: Replying to <small>&gt;&gt;{0}</small>"
+msgstr "Tryb postowania: Odpowiedź na <small>&gt;&gt;{0}</small>"
+
+#: ../../../../js/quick-reply.js:32 ../../../../js/quick-reply.js:33
+msgid "Return"
+msgstr "Powrót"
+
+#: ../../../../js/expand.js:20
+msgid "Click reply to view."
+msgstr "Kliknij Odpowiedź aby zobaczyć."
+
+#: ../../../../js/expand.js:20
+msgid "Click to expand"
+msgstr "Kliknij aby rozwinąć"
+
+#: ../../../../js/expand.js:41
+msgid "Hide expanded replies"
+msgstr "Schowaj rozwinięte odpowiedzi"
+
+#: ../../../../js/local-time.js:40
+msgid "Mon"
+msgstr "pon"
+
+#: ../../../../js/local-time.js:40
+msgid "Tue"
+msgstr "wto"
+
+#: ../../../../js/local-time.js:40
+msgid "Wed"
+msgstr "śro"
+
+#: ../../../../js/local-time.js:40
+msgid "Thu"
+msgstr "czw"
+
+#: ../../../../js/local-time.js:40
+msgid "Fri"
+msgstr "pią"
+
+#: ../../../../js/local-time.js:40
+msgid "Sat"
+msgstr "sob"
+
+#: ../../../../js/local-time.js:40
+msgid "Sun"
+msgstr "nie"
+
+#: ../../../../js/toggle-locked-threads.js:39
+#: ../../../../js/toggle-locked-threads.js:54
+msgid "Show locked threads"
+msgstr "Pokaż zablokowane tematy"
+
+#: ../../../../js/toggle-locked-threads.js:39
+#: ../../../../js/toggle-locked-threads.js:54
+msgid "Hide locked threads"
+msgstr "Schowaj zablokowane tematy"
+
+#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:65
+#: ../../../../js/forced-anon.js:69
+msgid "Forced anonymity"
+msgstr "Wymuszona anonimowość"
+
+#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:65
+msgid "enabled"
+msgstr "włączona"
+
+#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:69
+msgid "disabled"
+msgstr "wyłączona"
+
+#: ../../../../js/quick-post-controls.js:27
+msgid "Password"
+msgstr "Hasło"
+
+#: ../../../../js/quick-post-controls.js:29
+msgid "Delete file only"
+msgstr "Usuń tylko plik"
+
+#: ../../../../js/quick-post-controls.js:30
+msgid "File"
+msgstr "Plik"
+
+#: ../../../../js/quick-post-controls.js:31
+msgid "Delete"
+msgstr "Usuń"
+
+#: ../../../../js/quick-post-controls.js:35
+msgid "Reason"
+msgstr "Powód"
+
+#: ../../../../js/quick-post-controls.js:37
+msgid "Report"
+msgstr "Zgłoszenie"
diff --git a/inc/locale/pl_PL/LC_MESSAGES/tinyboard.mo b/inc/locale/pl_PL/LC_MESSAGES/tinyboard.mo
index 98f2ab5c..ac57873e 100644
--- a/inc/locale/pl_PL/LC_MESSAGES/tinyboard.mo
+++ b/inc/locale/pl_PL/LC_MESSAGES/tinyboard.mo
Binary files differ
diff --git a/inc/locale/pl_PL/LC_MESSAGES/tinyboard.po b/inc/locale/pl_PL/LC_MESSAGES/tinyboard.po
index 374278df..9bc9eece 100644
--- a/inc/locale/pl_PL/LC_MESSAGES/tinyboard.po
+++ b/inc/locale/pl_PL/LC_MESSAGES/tinyboard.po
@@ -1,31 +1,38 @@
-# Polish Tinyboard Translation
-# Copyright (C) 2011 Marcin Łabanowski
-# Ten plik jest wydany na takiej samej licencji co paczka PACKAGE.
-# Marcin Łabanowski (2011) <marcin@6irc.net>
+# 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 <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: 0.9.6-dev-6\n"
-"Report-Msgid-Bugs-To: marcin@6irc.net\n"
-"POT-Creation-Date: 2012-12-18 04:43+0100\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-18 16:31-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: POLISH <pl@li.org>\n"
-"Language: Polish\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \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==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2;\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+#. There is no previous page.
#: /var/www/html/Tinyboard/inc/functions.php:958
#: /var/www/html/Tinyboard/inc/functions.php:972
#: ../../../../inc/functions.php:1041 ../../../../inc/functions.php:1055
+#: ../../../../inc/functions.php:1039 ../../../../inc/functions.php:1053
+#: ../../../../inc/functions.php:1068 ../../../../inc/functions.php:1082
msgid "Previous"
msgstr "Wstecz"
+#. There is no next page.
#: /var/www/html/Tinyboard/inc/functions.php:977
#: /var/www/html/Tinyboard/inc/functions.php:986
#: ../../../../inc/functions.php:1060 ../../../../inc/functions.php:1069
+#: ../../../../inc/functions.php:1058 ../../../../inc/functions.php:1067
+#: ../../../../inc/functions.php:1087 ../../../../inc/functions.php:1096
msgid "Next"
msgstr "Dalej"
@@ -62,171 +69,216 @@ msgstr "Odpowiedz"
msgid "Quick Reply"
msgstr "Szybka odpowiedź"
+#.
+#. * ====================
+#. * Error messages
+#. * ====================
+#.
+#. Error messages
#: /var/www/html/Tinyboard/inc/config.php:600 ../../../../inc/config.php:638
+#: ../../../../inc/config.php:692 ../../../../inc/config.php:698
msgid "Lurk some more before posting."
msgstr "Nie postuj pierwszego dnia."
#: /var/www/html/Tinyboard/inc/config.php:601 ../../../../inc/config.php:639
+#: ../../../../inc/config.php:693 ../../../../inc/config.php:699
msgid "You look like a bot."
msgstr "Wyglądasz jak bot."
#: /var/www/html/Tinyboard/inc/config.php:602 ../../../../inc/config.php:640
+#: ../../../../inc/config.php:694 ../../../../inc/config.php:700
msgid "Your browser sent an invalid or no HTTP referer."
msgstr ""
"Twoja przeglądarka przesłała niepoprawny, bądź nie przesłała informacji o "
"odsyłaczu w nagłówku"
#: /var/www/html/Tinyboard/inc/config.php:603 ../../../../inc/config.php:641
+#: ../../../../inc/config.php:695 ../../../../inc/config.php:701
#, php-format
msgid "The %s field was too long."
msgstr "Pole %s jest za długie"
#: /var/www/html/Tinyboard/inc/config.php:604 ../../../../inc/config.php:642
+#: ../../../../inc/config.php:696 ../../../../inc/config.php:702
msgid "The body was too long."
msgstr "Zawartość jest za długa."
#: /var/www/html/Tinyboard/inc/config.php:605 ../../../../inc/config.php:643
+#: ../../../../inc/config.php:697 ../../../../inc/config.php:703
msgid "The body was too short or empty."
msgstr "Zawartość jest za krótka, bądź pusta."
#: /var/www/html/Tinyboard/inc/config.php:606 ../../../../inc/config.php:644
+#: ../../../../inc/config.php:698 ../../../../inc/config.php:704
msgid "You must upload an image."
msgstr "Musisz wysłać obrazek."
#: /var/www/html/Tinyboard/inc/config.php:607 ../../../../inc/config.php:645
+#: ../../../../inc/config.php:699 ../../../../inc/config.php:705
msgid "The server failed to handle your upload."
msgstr "Nie udało się obsłużyć twojego pliku."
#: /var/www/html/Tinyboard/inc/config.php:608 ../../../../inc/config.php:646
+#: ../../../../inc/config.php:700 ../../../../inc/config.php:706
msgid "Unsupported image format."
msgstr "Niewspierany format obrazka."
#: /var/www/html/Tinyboard/inc/config.php:609 ../../../../inc/config.php:647
+#: ../../../../inc/config.php:701 ../../../../inc/config.php:707
msgid "Invalid board!"
msgstr "Niepoprawny board!"
#: /var/www/html/Tinyboard/inc/config.php:610 ../../../../inc/config.php:648
+#: ../../../../inc/config.php:702 ../../../../inc/config.php:708
msgid "Thread specified does not exist."
msgstr "Wybrany wątek nie istnieje."
#: /var/www/html/Tinyboard/inc/config.php:611 ../../../../inc/config.php:649
+#: ../../../../inc/config.php:703 ../../../../inc/config.php:709
msgid "Thread locked. You may not reply at this time."
msgstr "Wątek jest zablokowany. Nie możesz w nim teraz postować."
#: /var/www/html/Tinyboard/inc/config.php:612 ../../../../inc/config.php:650
+#: ../../../../inc/config.php:706 ../../../../inc/config.php:712
msgid "You didn't make a post."
msgstr "Nie zrobiłeś posta."
#: /var/www/html/Tinyboard/inc/config.php:613 ../../../../inc/config.php:651
+#: ../../../../inc/config.php:707 ../../../../inc/config.php:713
msgid "Flood detected; Post discarded."
msgstr "Wykryto flood; Post odrzucony."
#: /var/www/html/Tinyboard/inc/config.php:614 ../../../../inc/config.php:652
+#: ../../../../inc/config.php:708 ../../../../inc/config.php:714
msgid "Your request looks automated; Post discarded."
msgstr "Twoje żądanie wygląda na zautomatyzowane; Post odrzucony."
#: /var/www/html/Tinyboard/inc/config.php:615 ../../../../inc/config.php:653
+#: ../../../../inc/config.php:709 ../../../../inc/config.php:715
msgid "Unoriginal content!"
msgstr "Nieoryginalna treść!"
#: /var/www/html/Tinyboard/inc/config.php:616 ../../../../inc/config.php:654
+#: ../../../../inc/config.php:710 ../../../../inc/config.php:716
#, php-format
msgid "Unoriginal content! You have been muted for %d seconds."
msgstr "Nieoryginalna treść! Zostałeś zagłuszony na %d sekund."
#: /var/www/html/Tinyboard/inc/config.php:617 ../../../../inc/config.php:655
+#: ../../../../inc/config.php:711 ../../../../inc/config.php:717
#, php-format
msgid "You are muted! Expires in %d seconds."
msgstr "Jesteś zagłuszony! Wygasa w ciągu %d sekund."
#: /var/www/html/Tinyboard/inc/config.php:618 ../../../../inc/config.php:656
+#: ../../../../inc/config.php:712 ../../../../inc/config.php:718
#, php-format
msgid "Your IP address is listed in %s."
msgstr "Twój adres IP jest na liście %s."
#: /var/www/html/Tinyboard/inc/config.php:619 ../../../../inc/config.php:657
+#: ../../../../inc/config.php:713 ../../../../inc/config.php:719
msgid "Too many links; flood detected."
msgstr "Zbyt dużo linków; wykryto flood."
#: /var/www/html/Tinyboard/inc/config.php:620 ../../../../inc/config.php:658
+#: ../../../../inc/config.php:714 ../../../../inc/config.php:720
msgid "Too many cites; post discarded."
msgstr "Zbyt dużo cytatów; post odrzucony."
#: /var/www/html/Tinyboard/inc/config.php:621 ../../../../inc/config.php:659
+#: ../../../../inc/config.php:715 ../../../../inc/config.php:721
msgid "Too many cross-board links; post discarded."
msgstr "Zbyt dużo linków między boardami; post odrzucony."
#: /var/www/html/Tinyboard/inc/config.php:622 ../../../../inc/config.php:660
+#: ../../../../inc/config.php:716 ../../../../inc/config.php:722
msgid "You didn't select anything to delete."
msgstr "Nie wybrano nic do usunięcia."
#: /var/www/html/Tinyboard/inc/config.php:623 ../../../../inc/config.php:661
+#: ../../../../inc/config.php:717 ../../../../inc/config.php:723
msgid "You didn't select anything to report."
msgstr "Nie wybrano nic do zgłoszenia."
#: /var/www/html/Tinyboard/inc/config.php:624 ../../../../inc/config.php:662
+#: ../../../../inc/config.php:718 ../../../../inc/config.php:724
msgid "You can't report that many posts at once."
msgstr "Nie możesz raportować tyle postów na raz."
#: /var/www/html/Tinyboard/inc/config.php:625 ../../../../inc/config.php:663
+#: ../../../../inc/config.php:719 ../../../../inc/config.php:725
msgid "Wrong password…"
msgstr "Niepoprawne hasło"
#: /var/www/html/Tinyboard/inc/config.php:626 ../../../../inc/config.php:664
+#: ../../../../inc/config.php:720 ../../../../inc/config.php:726
msgid "Invalid image."
msgstr "Niepoprawny obrazek."
#: /var/www/html/Tinyboard/inc/config.php:627 ../../../../inc/config.php:665
+#: ../../../../inc/config.php:721 ../../../../inc/config.php:727
msgid "Unknown file extension."
msgstr "Nieznane rozszerzenie pliku."
#: /var/www/html/Tinyboard/inc/config.php:628 ../../../../inc/config.php:666
+#: ../../../../inc/config.php:722 ../../../../inc/config.php:728
msgid "Maximum file size: %maxsz% bytes<br>Your file's size: %filesz% bytes"
msgstr ""
"Maksymalny rozmiar pliku: %maxsz% bajtów<br>Rozmiar twojego pliku: %filesz% "
"bajtów"
#: /var/www/html/Tinyboard/inc/config.php:629 ../../../../inc/config.php:667
+#: ../../../../inc/config.php:723 ../../../../inc/config.php:729
msgid "The file was too big."
msgstr "Plik jest za duży."
#: /var/www/html/Tinyboard/inc/config.php:630 ../../../../inc/config.php:668
+#: ../../../../inc/config.php:724 ../../../../inc/config.php:730
msgid "Invalid archive!"
msgstr "Niepoprawne archiwum!"
#: /var/www/html/Tinyboard/inc/config.php:631 ../../../../inc/config.php:669
+#: ../../../../inc/config.php:725 ../../../../inc/config.php:731
#, php-format
msgid "That file <a href=\"%s\">already exists</a>!"
msgstr "Ten plik <a href=\"%s\">już istnieje</a>!"
#: /var/www/html/Tinyboard/inc/config.php:632 ../../../../inc/config.php:670
+#: ../../../../inc/config.php:727 ../../../../inc/config.php:733
#, php-format
msgid "You'll have to wait another %s before deleting that."
msgstr "Musisz poczekać kolejne %s przed usunięciem tego."
#: /var/www/html/Tinyboard/inc/config.php:633 ../../../../inc/config.php:671
+#: ../../../../inc/config.php:728 ../../../../inc/config.php:734
msgid "MIME type detection XSS exploit (IE) detected; post discarded."
msgstr ""
"Wykryto próbę wykorzystania luki wykrywania typu MIME (XSS w IE); post "
"odrzucony"
#: /var/www/html/Tinyboard/inc/config.php:634 ../../../../inc/config.php:672
+#: ../../../../inc/config.php:729 ../../../../inc/config.php:735
msgid "Couldn't make sense of the URL of the video you tried to embed."
msgstr "Nie można było zrozumieć URL-a wideo, którego próbowano zapostować."
#: /var/www/html/Tinyboard/inc/config.php:635 ../../../../inc/config.php:673
+#: ../../../../inc/config.php:730 ../../../../inc/config.php:736
msgid "You seem to have mistyped the verification."
msgstr "Wygląda na to, że przepisano źle weryfikację."
#: /var/www/html/Tinyboard/inc/config.php:638 ../../../../inc/config.php:676
+#: ../../../../inc/config.php:734 ../../../../inc/config.php:740
msgid "Invalid username and/or password."
msgstr "Błędna nazwa użytkownika, bądź hasło"
#: /var/www/html/Tinyboard/inc/config.php:639 ../../../../inc/config.php:677
+#: ../../../../inc/config.php:735 ../../../../inc/config.php:741
msgid "You are not a mod…"
msgstr "Nie jesteś moderatorem"
#: /var/www/html/Tinyboard/inc/config.php:640 ../../../../inc/config.php:678
+#: ../../../../inc/config.php:736 ../../../../inc/config.php:742
msgid ""
"Invalid username and/or password. Your user may have been deleted or changed."
msgstr ""
@@ -234,46 +286,56 @@ msgstr ""
"albo zmienione."
#: /var/www/html/Tinyboard/inc/config.php:641 ../../../../inc/config.php:679
+#: ../../../../inc/config.php:737 ../../../../inc/config.php:743
msgid "Invalid/malformed cookies."
msgstr "Niepoprawne/zmodyfikowane pliki cookie."
#: /var/www/html/Tinyboard/inc/config.php:642 ../../../../inc/config.php:680
+#: ../../../../inc/config.php:738 ../../../../inc/config.php:744
msgid "Your browser didn't submit an input when it should have."
msgstr "Twoja przeglądarka nie wysłała pola, kiedy powinna."
#: /var/www/html/Tinyboard/inc/config.php:643 ../../../../inc/config.php:681
+#: ../../../../inc/config.php:739 ../../../../inc/config.php:745
#, php-format
msgid "The %s field is required."
msgstr "Pole %s jest wymagane."
#: /var/www/html/Tinyboard/inc/config.php:644 ../../../../inc/config.php:682
+#: ../../../../inc/config.php:740 ../../../../inc/config.php:746
#, php-format
msgid "The %s field was invalid."
msgstr "Pole %s jest niepoprawne."
#: /var/www/html/Tinyboard/inc/config.php:645 ../../../../inc/config.php:683
+#: ../../../../inc/config.php:741 ../../../../inc/config.php:747
#, php-format
msgid "There is already a %s board."
msgstr "Już istnieje board %s"
#: /var/www/html/Tinyboard/inc/config.php:646 ../../../../inc/config.php:684
+#: ../../../../inc/config.php:742 ../../../../inc/config.php:748
msgid "You don't have permission to do that."
msgstr "Nie masz uprawnień do wykonania tej czynności."
#: /var/www/html/Tinyboard/inc/config.php:647 ../../../../inc/config.php:685
+#: ../../../../inc/config.php:743 ../../../../inc/config.php:749
msgid "That post doesn't exist…"
msgstr "Ten post nie istnieje..."
#: /var/www/html/Tinyboard/inc/config.php:648 ../../../../inc/config.php:686
+#: ../../../../inc/config.php:744 ../../../../inc/config.php:750
msgid "Page not found."
msgstr "Strona nie znaleziona."
#: /var/www/html/Tinyboard/inc/config.php:649 ../../../../inc/config.php:687
+#: ../../../../inc/config.php:745 ../../../../inc/config.php:751
#, php-format
msgid "That mod <a href=\"?/users/%d\">already exists</a>!"
msgstr "Ten moderator <a href=\"?/users/%d\">już istnieje</a>!"
#: /var/www/html/Tinyboard/inc/config.php:650 ../../../../inc/config.php:688
+#: ../../../../inc/config.php:746 ../../../../inc/config.php:752
msgid "That theme doesn't exist!"
msgstr "Ten dodatek nie istnieje!"
@@ -331,7 +393,7 @@ msgid "Go back"
msgstr "Wróć"
#: /var/www/html/Tinyboard/inc/display.php:97 ../../../../inc/display.php:91
-#: ../../../../inc/mod/pages.php:59
+#: ../../../../inc/mod/pages.php:59 ../../../../inc/mod/pages.php:62
msgid "Login"
msgstr "Logowanie"
@@ -343,10 +405,12 @@ msgstr ""
"Post za długi. Kliknij <a href=\"%s\">tutaj</a> aby zobaczyć pełnego posta.</"
"span>"
+#. line 5
#: /var/www/html/Tinyboard/inc/display.php:272
#: /var/www/html/Tinyboard/inc/display.php:365
#: /var/www/html/Tinyboard/templates/cache/dd/1a/77e08f0c1b4ecf707c5a3e5a70be.php:27
#: ../../../../templates/cache/a8/a6/1022091d3402e085395b12e6279a.php:27
+#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:27
msgid "Delete"
msgstr "Usuń"
@@ -382,6 +446,7 @@ msgstr "Usuń plik"
#: /var/www/html/Tinyboard/inc/display.php:292
#: /var/www/html/Tinyboard/inc/display.php:408
+#: ../../../../inc/mod/pages.php:1080 ../../../../inc/mod/pages.php:1105
msgid "Edit post"
msgstr "Edytuj post"
@@ -413,21 +478,27 @@ msgstr "Zablokuj wątek"
msgid "Move thread to another board"
msgstr "Przenieś wątek na inny board"
+#. line 11
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:48
#: /var/www/html/Tinyboard/mod.php:667 /var/www/html/Tinyboard/mod.php:750
#: /var/www/html/Tinyboard/mod.php:833
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:55
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:55
msgid "Name"
msgstr "Nazwa"
+#. line 21
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:62
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:76
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:76
msgid "Email"
msgstr "E-mail"
+#. line 34
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:96
#: /var/www/html/Tinyboard/mod.php:753 /var/www/html/Tinyboard/mod.php:839
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:95
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:116
msgid "Subject"
msgstr "Temat"
@@ -435,71 +506,347 @@ msgstr "Temat"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:119
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:112
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:117
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:119
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:100
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:133
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:172
msgid "Spoiler Image"
msgstr "Schowaj obrazek"
+#. line 45
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:119
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:121
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:145
msgid "Comment"
msgstr "Komentarz"
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
+#. line 61
+#. line 73
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:133
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:142
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:191
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:217
msgid "Verification"
msgstr "Weryfikacja"
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
+#. line 3
+#. line 87
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:149
#: /var/www/html/Tinyboard/templates/cache/dd/1a/77e08f0c1b4ecf707c5a3e5a70be.php:22
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:165
#: ../../../../templates/cache/a8/a6/1022091d3402e085395b12e6279a.php:22
+#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:22
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:250
msgid "File"
msgstr "Plik"
+#. line 97
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:163
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:183
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:268
msgid "Embed"
msgstr "Osadź"
+#. line 109
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:179
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:206
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:291
msgid "Flags"
msgstr "Flagi"
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
+#. line 113
+#. line 114
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:188
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:191
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:215
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:218
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:300
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:303
msgid "Sticky"
msgstr "Przyklejony"
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
+#. line 117
+#. line 118
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:200
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:203
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:227
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:230
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:312
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:315
msgid "Lock"
msgstr "Zablokowany"
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
+#. line 121
+#. line 122
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:212
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:215
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:239
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:242
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:324
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:327
msgid "Raw HTML"
msgstr "Czysty HTML"
+#. line 129
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:230
#: /var/www/html/Tinyboard/templates/cache/dd/1a/77e08f0c1b4ecf707c5a3e5a70be.php:23
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:257
#: ../../../../templates/cache/a8/a6/1022091d3402e085395b12e6279a.php:23
+#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:23
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:342
msgid "Password"
msgstr "Hasło"
+#. line 134
#: /var/www/html/Tinyboard/templates/cache/1d/5e/91ec2bc929b77377b8b877d82db4.php:236
#: ../../../../templates/cache/d1/2d/eda9403e966240c642b13ca43eb6.php:266
+#: ../../../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:351
msgid "(For file deletion.)"
msgstr "(do usuwania postów)"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:107
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:105
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:107
msgid "File:"
msgstr "Plik:"
@@ -518,11 +865,15 @@ msgstr "Odśwież"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:478
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:415
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:461
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:472
msgid "Reply"
msgstr "Odpowiedź"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:511
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:442
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:488
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:499
msgid "1 post"
msgid_plural "%count% posts"
msgstr[0] "1 post"
@@ -531,11 +882,15 @@ msgstr[2] "%count% postów"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:517
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:448
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:494
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:505
msgid "and"
msgstr "oraz"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:528
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:459
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:505
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:516
msgid "1 image reply"
msgid_plural "%count% image replies"
msgstr[0] "1 obrazek"
@@ -544,6 +899,8 @@ msgstr[2] "%count% obrazków"
#: /var/www/html/Tinyboard/templates/cache/96/13/d13c7abb8d82989e547dc9be1787.php:533
#: ../../../../templates/cache/cf/0c/61af144f478f5c035cb3a2799e48.php:464
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:510
+#: ../../../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:521
msgid "omitted. Click reply to view."
msgstr "pominięte. Kliknij Odpowiedź aby zobaczyć."
@@ -552,11 +909,20 @@ msgstr "pominięte. Kliknij Odpowiedź aby zobaczyć."
#: /var/www/html/Tinyboard/templates/cache/0b/22/d0c24fb343dd5fe77600d77dcc1b.php:159
#: ../../../../templates/cache/82/20/1c3352a2eb8f4503c0f7634bca15.php:169
#: ../../../../templates/cache/7a/d3/9236b821893e6bc57b16919988fd.php:169
+#: ../../../../templates/cache/f5/e3/343716327c6183713f70a3fb57f1.php:121
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:98
+#: ../../../../templates/cache/62/8c/21348d46377c3e1b3f8c476ba376.php:62
+#: ../../../../templates/cache/f5/e3/343716327c6183713f70a3fb57f1.php:131
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:115
msgid "Return to dashboard"
msgstr "Powróć na tablicę"
+#. line 27
+#. line 31
#: /var/www/html/Tinyboard/templates/cache/0b/22/d0c24fb343dd5fe77600d77dcc1b.php:165
#: ../../../../templates/cache/82/20/1c3352a2eb8f4503c0f7634bca15.php:177
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:106
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:123
msgid "Posting mode: Reply"
msgstr "Tryb postowania: Odpowiedź"
@@ -564,22 +930,32 @@ msgstr "Tryb postowania: Odpowiedź"
#: /var/www/html/Tinyboard/templates/cache/0b/22/d0c24fb343dd5fe77600d77dcc1b.php:210
#: ../../../../templates/cache/82/20/1c3352a2eb8f4503c0f7634bca15.php:180
#: ../../../../templates/cache/82/20/1c3352a2eb8f4503c0f7634bca15.php:222
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:109
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:155
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:126
+#: ../../../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:172
msgid "Return"
msgstr "Powrót"
+#. line 2
#: /var/www/html/Tinyboard/templates/cache/dd/1a/77e08f0c1b4ecf707c5a3e5a70be.php:19
#: ../../../../templates/cache/a8/a6/1022091d3402e085395b12e6279a.php:19
+#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:19
msgid "Delete Post"
msgstr "Usuń post"
+#. line 8
#: /var/www/html/Tinyboard/templates/cache/dd/1a/77e08f0c1b4ecf707c5a3e5a70be.php:32
#: /var/www/html/Tinyboard/mod.php:1801
#: ../../../../templates/cache/a8/a6/1022091d3402e085395b12e6279a.php:32
+#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:32
msgid "Reason"
msgstr "Powód"
+#. line 10
#: /var/www/html/Tinyboard/templates/cache/dd/1a/77e08f0c1b4ecf707c5a3e5a70be.php:36
#: ../../../../templates/cache/a8/a6/1022091d3402e085395b12e6279a.php:36
+#: ../../../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:36
msgid "Report"
msgstr "Zgłoszenie"
@@ -597,22 +973,26 @@ msgid "PM Inbox"
msgstr "Wiadomości prywatne"
#: /var/www/html/Tinyboard/mod.php:136 /var/www/html/Tinyboard/mod.php:874
-#: ../../../../inc/mod/pages.php:376
+#: ../../../../inc/mod/pages.php:376 ../../../../inc/mod/pages.php:393
+#: ../../../../inc/mod/pages.php:406
msgid "News"
msgstr "Aktualności"
#: /var/www/html/Tinyboard/mod.php:141 /var/www/html/Tinyboard/mod.php:1614
-#: ../../../../inc/mod/pages.php:1557
+#: ../../../../inc/mod/pages.php:1557 ../../../../inc/mod/pages.php:1657
+#: ../../../../inc/mod/pages.php:1682
msgid "Report queue"
msgstr "Kolejka zgłoszeń"
#: /var/www/html/Tinyboard/mod.php:144 /var/www/html/Tinyboard/mod.php:1882
-#: ../../../../inc/mod/pages.php:664
+#: ../../../../inc/mod/pages.php:664 ../../../../inc/mod/pages.php:705
+#: ../../../../inc/mod/pages.php:721
msgid "Ban list"
msgstr "Lista banów"
#: /var/www/html/Tinyboard/mod.php:147 /var/www/html/Tinyboard/mod.php:1288
-#: ../../../../inc/mod/pages.php:1271
+#: ../../../../inc/mod/pages.php:1271 ../../../../inc/mod/pages.php:1369
+#: ../../../../inc/mod/pages.php:1394
msgid "Manage users"
msgstr "Zarządzaj użytkownikami"
@@ -622,6 +1002,8 @@ msgstr "Zmień swoje hasło"
#: /var/www/html/Tinyboard/mod.php:152 /var/www/html/Tinyboard/mod.php:477
#: ../../../../inc/mod/pages.php:416 ../../../../inc/mod/pages.php:443
+#: ../../../../inc/mod/pages.php:433 ../../../../inc/mod/pages.php:460
+#: ../../../../inc/mod/pages.php:446 ../../../../inc/mod/pages.php:473
msgid "Moderation log"
msgstr "Log moderacji"
@@ -638,7 +1020,8 @@ msgid "Show configuration"
msgstr "Pokaż konfigurację"
#: /var/www/html/Tinyboard/mod.php:165 /var/www/html/Tinyboard/mod.php:709
-#: ../../../../inc/mod/pages.php:1739
+#: ../../../../inc/mod/pages.php:1739 ../../../../inc/mod/pages.php:1805
+#: ../../../../inc/mod/pages.php:1830
msgid "Manage themes"
msgstr "Zarządzaj dodatkami"
@@ -660,6 +1043,7 @@ msgstr ""
"gwiazdki (*) jako znak wieloznaczny."
#: /var/www/html/Tinyboard/mod.php:180 ../../../../inc/mod/pages.php:106
+#: ../../../../inc/mod/pages.php:110
msgid "Could not find current version! (Check .installed)"
msgstr "Nie można znaleźć obecnej wersji! (Sprawdź .installed)"
@@ -668,6 +1052,7 @@ msgid "Logout"
msgstr "Wyloguj"
#: /var/www/html/Tinyboard/mod.php:245 ../../../../inc/mod/pages.php:147
+#: ../../../../inc/mod/pages.php:162
msgid "Dashboard"
msgstr "Tablica"
@@ -692,10 +1077,12 @@ msgid "Action"
msgstr "Akcja"
#: /var/www/html/Tinyboard/mod.php:528 ../../../../inc/mod/pages.php:1723
+#: ../../../../inc/mod/pages.php:1789 ../../../../inc/mod/pages.php:1814
msgid "Themes directory doesn't exist!"
msgstr "Katalog dodatków (themes) nie istnieje!"
#: /var/www/html/Tinyboard/mod.php:530 ../../../../inc/mod/pages.php:1725
+#: ../../../../inc/mod/pages.php:1791 ../../../../inc/mod/pages.php:1816
msgid "Cannot open themes directory; check permissions."
msgstr "Nie można otworzyć katalogu dodatków (themes); sprawdź uprawnienia."
@@ -728,7 +1115,9 @@ msgid "Install"
msgstr "Instaluj"
#: /var/www/html/Tinyboard/mod.php:693 ../../../../inc/mod/pages.php:1467
-#: ../../../../inc/mod/pages.php:1471
+#: ../../../../inc/mod/pages.php:1471 ../../../../inc/mod/pages.php:1567
+#: ../../../../inc/mod/pages.php:1571 ../../../../inc/mod/pages.php:1592
+#: ../../../../inc/mod/pages.php:1596
msgid "Rebuild"
msgstr "Przebuduj"
@@ -749,6 +1138,7 @@ msgid "Post to noticeboard"
msgstr "Postuj na tablicy ogłoszeń"
#: /var/www/html/Tinyboard/mod.php:792 ../../../../inc/mod/pages.php:316
+#: ../../../../inc/mod/pages.php:333 ../../../../inc/mod/pages.php:346
msgid "Noticeboard"
msgstr "Tablica ogłoszeń"
@@ -818,10 +1208,12 @@ msgid "Configuration"
msgstr "Konfiguracja"
#: /var/www/html/Tinyboard/mod.php:2174 ../../../../inc/mod/pages.php:255
+#: ../../../../inc/mod/pages.php:272 ../../../../inc/mod/pages.php:285
msgid "Couldn't open board after creation."
msgstr "Nie można otworzyć boardu po utworzeniu."
#: /var/www/html/Tinyboard/mod.php:2678 ../../../../inc/mod/pages.php:759
+#: ../../../../inc/mod/pages.php:800 ../../../../inc/mod/pages.php:823
msgid "Target and source board are the same."
msgstr "Docelowy i źródłowy board są takie same."
@@ -829,73 +1221,137 @@ msgstr "Docelowy i źródłowy board są takie same."
msgid "No board to move to; there is only one."
msgstr "Nie ma boardu na który można to przenieść; istnieje tylko jeden."
-#: ../../../../inc/config.php:689
+#: ../../../../inc/config.php:689 ../../../../inc/config.php:747
+#: ../../../../inc/config.php:753
msgid "Invalid security token! Please go back and try again."
msgstr "Niepoprawny token bezpieczeństwa! Proszę cofnąć i spróbować ponownie."
-#: ../../../../inc/mod/pages.php:63
+#: ../../../../inc/mod/pages.php:63 ../../../../inc/mod/pages.php:66
msgid "Confirm action"
msgstr "Potwierdź akcję"
-#: ../../../../inc/mod/pages.php:222
+#: ../../../../inc/mod/pages.php:222 ../../../../inc/mod/pages.php:239
+#: ../../../../inc/mod/pages.php:252
msgid "Edit board"
msgstr "Edytuj board"
-#: ../../../../inc/mod/pages.php:270
+#: ../../../../inc/mod/pages.php:270 ../../../../inc/mod/pages.php:287
+#: ../../../../inc/mod/pages.php:300
msgid "New board"
msgstr "Nowy board"
-#: ../../../../inc/mod/pages.php:586
+#: ../../../../inc/mod/pages.php:586 ../../../../inc/mod/pages.php:612
+#: ../../../../inc/mod/pages.php:628
msgid "IP"
msgstr "adres IP"
#: ../../../../inc/mod/pages.php:596 ../../../../inc/mod/pages.php:985
+#: ../../../../inc/mod/pages.php:622 ../../../../inc/mod/pages.php:1028
+#: ../../../../inc/mod/pages.php:638 ../../../../inc/mod/pages.php:1053
msgid "New ban"
msgstr "Nowy ban"
-#: ../../../../inc/mod/pages.php:919
+#: ../../../../inc/mod/pages.php:919 ../../../../inc/mod/pages.php:962
+#: ../../../../inc/mod/pages.php:987
msgid "Impossible to move thread; there is only one board."
msgstr "Nie można przenieść wątku; istnieje tylko jeden board."
-#: ../../../../inc/mod/pages.php:923
+#: ../../../../inc/mod/pages.php:923 ../../../../inc/mod/pages.php:966
+#: ../../../../inc/mod/pages.php:991
msgid "Move thread"
msgstr "Przenieś wątek"
#: ../../../../inc/mod/pages.php:1209 ../../../../inc/mod/pages.php:1258
+#: ../../../../inc/mod/pages.php:1307 ../../../../inc/mod/pages.php:1356
+#: ../../../../inc/mod/pages.php:1332 ../../../../inc/mod/pages.php:1381
msgid "Edit user"
msgstr "Edytuj użytkownika"
+#. deleted?
#: ../../../../inc/mod/pages.php:1333 ../../../../inc/mod/pages.php:1405
+#: ../../../../inc/mod/pages.php:1431 ../../../../inc/mod/pages.php:1503
+#: ../../../../inc/mod/pages.php:1456 ../../../../inc/mod/pages.php:1528
msgid "New PM for"
msgstr "Nowe PW dla"
-#: ../../../../inc/mod/pages.php:1337
+#: ../../../../inc/mod/pages.php:1337 ../../../../inc/mod/pages.php:1435
+#: ../../../../inc/mod/pages.php:1460
msgid "Private message"
msgstr "Prywatna wiadomość"
-#: ../../../../inc/mod/pages.php:1358
+#: ../../../../inc/mod/pages.php:1358 ../../../../inc/mod/pages.php:1456
+#: ../../../../inc/mod/pages.php:1481
msgid "PM inbox"
msgstr "Odebrane PW"
-#: ../../../../inc/mod/pages.php:1679
+#: ../../../../inc/mod/pages.php:1679 ../../../../inc/mod/pages.php:1779
+#: ../../../../inc/mod/pages.php:1804
msgid "Config editor"
msgstr "Edytor konfiguracji"
-#: ../../../../inc/mod/pages.php:1713
+#: ../../../../inc/mod/pages.php:1713 ../../../../inc/mod/pages.php:1945
+#: ../../../../inc/mod/pages.php:1970
msgid "Debug: Anti-spam"
msgstr "Debug: Antyspam"
-#: ../../../../inc/mod/pages.php:1801
+#: ../../../../inc/mod/pages.php:1801 ../../../../inc/mod/pages.php:1867
+#: ../../../../inc/mod/pages.php:1892
#, php-format
msgid "Installed theme: %s"
msgstr "Zainstalowano dodatek: %s"
-#: ../../../../inc/mod/pages.php:1811
+#: ../../../../inc/mod/pages.php:1811 ../../../../inc/mod/pages.php:1878
+#: ../../../../inc/mod/pages.php:1903
#, php-format
msgid "Configuring theme: %s"
msgstr "Konfigurowanie dodatku: %s"
-#: ../../../../inc/mod/pages.php:1839
+#: ../../../../inc/mod/pages.php:1839 ../../../../inc/mod/pages.php:1906
+#: ../../../../inc/mod/pages.php:1931
#, php-format
msgid "Rebuilt theme: %s"
msgstr "Przebudowano dodatek: %s"
+
+#: ../../../../inc/lib/gettext/examples/pigs_dropin.php:77
+msgid ""
+"This is how the story goes.\n"
+"\n"
+msgstr ""
+
+#: ../../../../inc/config.php:704 ../../../../inc/config.php:710
+msgid "Thread has reached its maximum reply limit."
+msgstr "Ten temat osiągnął swój maksymalny limit odpowiedzi."
+
+#: ../../../../inc/config.php:705 ../../../../inc/config.php:711
+msgid "Thread has reached its maximum image limit."
+msgstr "Ten temat osiągnął swój maksymalny limit obrazków."
+
+#: ../../../../inc/config.php:726 ../../../../inc/config.php:732
+#, php-format
+msgid "That file <a href=\"%s\">already exists</a> in this thread!"
+msgstr "Ten plik <a href=\"%s\">już istnieje</a> w tym temacie!"
+
+#. Moderator errors
+#: ../../../../inc/config.php:733 ../../../../inc/config.php:739
+#, php-format
+msgid ""
+"You are only allowed to unban %s users at a time. You tried to unban %u "
+"users."
+msgstr ""
+"Możesz odbanować tylko %s użytkowników na raz. Próbowałeś odbanować %u users."
+
+#: ../../../../inc/mod/pages.php:1969 ../../../../inc/mod/pages.php:1994
+msgid "Debug: Recent posts"
+msgstr "Debug: Ostatnie posty"
+
+#: ../../../../inc/mod/pages.php:1993 ../../../../inc/mod/pages.php:2018
+msgid "Debug: SQL"
+msgstr "Debug: SQL"
+
+#. line 28
+#: ../../../../templates/cache/4b/3e/915cc5ac5fe144c331207c656528.php:104
+msgid "1 reply"
+msgid_plural "%count% replies"
+msgstr[0] "1 odpowiedź"
+msgstr[1] "%count% odpowiedzi"
+msgstr[2] "%count% odpowiedzi"
diff --git a/inc/locale/pt_BR/LC_MESSAGES/tinyboard.mo b/inc/locale/pt_BR/LC_MESSAGES/tinyboard.mo
new file mode 100644
index 00000000..3bb6778b
--- /dev/null
+++ b/inc/locale/pt_BR/LC_MESSAGES/tinyboard.mo
Binary files differ
diff --git a/inc/locale/pt_BR/LC_MESSAGES/tinyboard.po b/inc/locale/pt_BR/LC_MESSAGES/tinyboard.po
new file mode 100644
index 00000000..c2ad38a2
--- /dev/null
+++ b/inc/locale/pt_BR/LC_MESSAGES/tinyboard.po
@@ -0,0 +1,844 @@
+# 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 <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-04-21 11:29-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural==(n != 1);n"
+
+#: ../../inc/lib/gettext/examples/pigs_dropin.php:77
+msgid ""
+"This is how the story goes.\n"
+"\n"
+msgstr "Era uma vez\n\n"
+
+#: ../../inc/functions.php:1046 ../../inc/functions.php:1060
+msgid "Previous"
+msgstr "Anterior"
+
+#: ../../inc/functions.php:1065 ../../inc/functions.php:1074
+msgid "Next"
+msgstr "Proximo"
+
+#: ../../inc/display.php:91 ../../inc/mod/pages.php:62
+msgid "Login"
+msgstr "Login"
+
+#: ../../inc/config.php:687
+msgid "Lurk some more before posting."
+msgstr "Lurke mais antes de postar."
+
+#: ../../inc/config.php:688
+msgid "You look like a bot."
+msgstr "Você não parece humano."
+
+#: ../../inc/config.php:689
+msgid "Your browser sent an invalid or no HTTP referer."
+msgstr "Seu browser enviou um referial HTTP inválido ou não enviou o referencial."
+
+#: ../../inc/config.php:690
+#, php-format
+msgid "The %s field was too long."
+msgstr "O campo %s é longo demais."
+
+#: ../../inc/config.php:691
+msgid "The body was too long."
+msgstr "O corpo do texto é longo demais."
+
+#: ../../inc/config.php:692
+msgid "The body was too short or empty."
+msgstr "O corpo do texto é pequeno demais ou inexistente."
+
+#: ../../inc/config.php:693
+msgid "You must upload an image."
+msgstr "Você deve fazer upload de uma imagem."
+
+#: ../../inc/config.php:694
+msgid "The server failed to handle your upload."
+msgstr "O servidor não conseguiu lidar com seu upload."
+
+#: ../../inc/config.php:695
+msgid "Unsupported image format."
+msgstr "Tipo de imagem não aceito."
+
+#: ../../inc/config.php:696
+msgid "Invalid board!"
+msgstr "Board inválida!"
+
+#: ../../inc/config.php:697
+msgid "Thread specified does not exist."
+msgstr "O tópico especificado não existe.."
+
+#: ../../inc/config.php:698
+msgid "Thread locked. You may not reply at this time."
+msgstr "Tópico trancado, você não pode postar."
+
+#: ../../inc/config.php:699
+msgid "You didn't make a post."
+msgstr "Você não escreveu uma mensagem."
+
+#: ../../inc/config.php:700
+msgid "Flood detected; Post discarded."
+msgstr "Flood detectado; Sua mensagem foi descartada."
+
+#: ../../inc/config.php:701
+msgid "Your request looks automated; Post discarded."
+msgstr "Sua requisição parece automatizada; Mensagem descartada."
+
+#: ../../inc/config.php:702
+msgid "Unoriginal content!"
+msgstr "Conteudo não original!"
+
+#: ../../inc/config.php:703
+#, php-format
+msgid "Unoriginal content! You have been muted for %d seconds."
+msgstr "Conteudo não original! Você está impedido de postar por %d segundos."
+
+#: ../../inc/config.php:704
+#, php-format
+msgid "You are muted! Expires in %d seconds."
+msgstr "Você está impedido de postar! Expira em %d segundos."
+
+#: ../../inc/config.php:705
+#, php-format
+msgid "Your IP address is listed in %s."
+msgstr "Seu IP está listado em %s."
+
+#: ../../inc/config.php:706
+msgid "Too many links; flood detected."
+msgstr "Links demais; Flood detectado."
+
+#: ../../inc/config.php:707
+msgid "Too many cites; post discarded."
+msgstr "Citações demais; Post descartado."
+
+#: ../../inc/config.php:708
+msgid "Too many cross-board links; post discarded."
+msgstr "Links entre boards demais; Post descartado."
+
+#: ../../inc/config.php:709
+msgid "You didn't select anything to delete."
+msgstr "Você não selecionou nada para deletar."
+
+#: ../../inc/config.php:710
+msgid "You didn't select anything to report."
+msgstr "Você não selecionou nada para denunciar."
+
+#: ../../inc/config.php:711
+msgid "You can't report that many posts at once."
+msgstr "Você não pode denunciar tantas mensagens ao mesmo tempo."
+
+#: ../../inc/config.php:712
+msgid "Wrong password…"
+msgstr "Senha incorreta…"
+
+#: ../../inc/config.php:713
+msgid "Invalid image."
+msgstr "Imagem inválida."
+
+#: ../../inc/config.php:714
+msgid "Unknown file extension."
+msgstr "Extenção de arquivo desconhecida."
+
+#: ../../inc/config.php:715
+msgid "Maximum file size: %maxsz% bytes<br>Your file's size: %filesz% bytes"
+msgstr "Tamanho maximo de arquivos: %maxsz% bytes<br>O tamanho do seu arquivo: %filesz% bytes"
+
+#: ../../inc/config.php:716
+msgid "The file was too big."
+msgstr "Seu arquivo é grande demais."
+
+#: ../../inc/config.php:717
+msgid "Invalid archive!"
+msgstr "Arquivo inválido!"
+
+#: ../../inc/config.php:718
+#, php-format
+msgid "That file <a href=\"%s\">already exists</a>!"
+msgstr "O arquivo <a href=\"%s\">já existe</a>!"
+
+#: ../../inc/config.php:719
+#, php-format
+msgid "That file <a href=\"%s\">already exists</a> in this thread!"
+msgstr "O arquivo <a href=\"%s\">já existe</a> neste tópico!"
+
+#: ../../inc/config.php:720
+#, php-format
+msgid "You'll have to wait another %s before deleting that."
+msgstr "Você terá que esperar %s segundos antes de deletar isso."
+
+#: ../../inc/config.php:721
+msgid "MIME type detection XSS exploit (IE) detected; post discarded."
+msgstr "Exploit XSS do tipo MIME (IE) detectado; mensagem descartada."
+
+#: ../../inc/config.php:722
+msgid "Couldn't make sense of the URL of the video you tried to embed."
+msgstr "Não consegui processar a URL do video que você tentou integrar"
+
+#: ../../inc/config.php:723
+msgid "You seem to have mistyped the verification."
+msgstr "Você errou o codigo de verificação."
+
+#: ../../inc/config.php:726
+msgid "Invalid username and/or password."
+msgstr "Login e/ou senha inválido(s)."
+
+#: ../../inc/config.php:727
+msgid "You are not a mod…"
+msgstr "Você não é mod…"
+
+#: ../../inc/config.php:728
+msgid ""
+"Invalid username and/or password. Your user may have been deleted or changed."
+msgstr "Login e/ou senha inválido(s). Seu login deve ter sido mudado ou removido."
+
+#: ../../inc/config.php:729
+msgid "Invalid/malformed cookies."
+msgstr "Cookies inválidos ou mal formados."
+
+#: ../../inc/config.php:730
+msgid "Your browser didn't submit an input when it should have."
+msgstr "Seu browser não enviou uma entrada quando ele deveria."
+
+#: ../../inc/config.php:731
+#, php-format
+msgid "The %s field is required."
+msgstr "O campo %s é necessário."
+
+#: ../../inc/config.php:732
+#, php-format
+msgid "The %s field was invalid."
+msgstr "O campo %s é inválido."
+
+#: ../../inc/config.php:733
+#, php-format
+msgid "There is already a %s board."
+msgstr "A board %s já existe."
+
+#: ../../inc/config.php:734
+msgid "You don't have permission to do that."
+msgstr "Você não tem permissão para fazer isso."
+
+#: ../../inc/config.php:735
+msgid "That post doesn't exist…"
+msgstr "Este post já existe…"
+
+#: ../../inc/config.php:736
+msgid "Page not found."
+msgstr "Pagina não encontrada."
+
+#: ../../inc/config.php:737
+#, php-format
+msgid "That mod <a href=\"?/users/%d\">already exists</a>!"
+msgstr "Este mod <a href=\"?/users/%d\">já existe</a>!"
+
+#: ../../inc/config.php:738
+msgid "That theme doesn't exist!"
+msgstr "Este tema não existe!"
+
+#: ../../inc/config.php:739
+msgid "Invalid security token! Please go back and try again."
+msgstr "Token de segurança inválido! Retorne e tente de novo."
+
+#: ../../inc/mod/pages.php:66
+msgid "Confirm action"
+msgstr "Confirmar ação"
+
+#: ../../inc/mod/pages.php:110
+msgid "Could not find current version! (Check .installed)"
+msgstr "Não foi possivel encontrar a versão atual! (Cheque o .installed)"
+
+#: ../../inc/mod/pages.php:151
+msgid "Dashboard"
+msgstr "Dashboard"
+
+#: ../../inc/mod/pages.php:228
+msgid "Edit board"
+msgstr "Editar board"
+
+#: ../../inc/mod/pages.php:261
+msgid "Couldn't open board after creation."
+msgstr "Não foi possivel abrir a board após a criação."
+
+#: ../../inc/mod/pages.php:276
+msgid "New board"
+msgstr "Nova board"
+
+#: ../../inc/mod/pages.php:322
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:96
+msgid "Noticeboard"
+msgstr "Quadro de noticias"
+
+#: ../../inc/mod/pages.php:382
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:166
+msgid "News"
+msgstr "Noticias"
+
+#: ../../inc/mod/pages.php:422 ../../inc/mod/pages.php:449
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:255
+msgid "Moderation log"
+msgstr "Log da moderação"
+
+#: ../../inc/mod/pages.php:592
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:247
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:65
+msgid "IP"
+msgstr "IP"
+
+#: ../../inc/mod/pages.php:602 ../../inc/mod/pages.php:993
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:377
+msgid "New ban"
+msgstr "Nova expulsão"
+
+#: ../../inc/mod/pages.php:670
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:224
+msgid "Ban list"
+msgstr "Lista de expulsões"
+
+#: ../../inc/mod/pages.php:765
+msgid "Target and source board are the same."
+msgstr "Board alvo e fonte são as mesmas."
+
+#: ../../inc/mod/pages.php:927
+msgid "Impossible to move thread; there is only one board."
+msgstr "Impossivel de mover o tópico; Só existe uma board."
+
+#: ../../inc/mod/pages.php:931
+msgid "Move thread"
+msgstr "Mover tópico"
+
+#: ../../inc/mod/pages.php:1045
+msgid "Edit post"
+msgstr "Editar mensagem"
+
+#: ../../inc/mod/pages.php:1271 ../../inc/mod/pages.php:1320
+msgid "Edit user"
+msgstr "Editar usuário"
+
+#: ../../inc/mod/pages.php:1333
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:232
+msgid "Manage users"
+msgstr "Administrar usuários"
+
+#: ../../inc/mod/pages.php:1395 ../../inc/mod/pages.php:1467
+msgid "New PM for"
+msgstr "Nova MP para"
+
+#: ../../inc/mod/pages.php:1399
+msgid "Private message"
+msgstr "Mensagem pessoal"
+
+#: ../../inc/mod/pages.php:1420
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:171
+msgid "PM inbox"
+msgstr "Entrada de MP"
+
+#: ../../inc/mod/pages.php:1531 ../../inc/mod/pages.php:1535
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:263
+msgid "Rebuild"
+msgstr "Reconstruir"
+
+#: ../../inc/mod/pages.php:1621
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:207
+msgid "Report queue"
+msgstr "Fila de denuncias"
+
+#: ../../inc/mod/pages.php:1743
+msgid "Config editor"
+msgstr "Editor de configurações"
+
+#: ../../inc/mod/pages.php:1753
+msgid "Themes directory doesn't exist!"
+msgstr "Diretório de temas não existe!"
+
+#: ../../inc/mod/pages.php:1755
+msgid "Cannot open themes directory; check permissions."
+msgstr "Não é possivel abrir diretorio de temas; reveja suas permissões."
+
+#: ../../inc/mod/pages.php:1769
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:247
+msgid "Manage themes"
+msgstr "Administrar temas"
+
+#: ../../inc/mod/pages.php:1831
+#, php-format
+msgid "Installed theme: %s"
+msgstr "Tema instalado: %s"
+
+#: ../../inc/mod/pages.php:1841
+#, php-format
+msgid "Configuring theme: %s"
+msgstr "Configurando tema: %s"
+
+#: ../../inc/mod/pages.php:1869
+#, php-format
+msgid "Rebuilt theme: %s"
+msgstr "Reconstruir tema: %s"
+
+#: ../../inc/mod/pages.php:1908
+msgid "Debug: Anti-spam"
+msgstr "Debug: Anti-spam"
+
+#: ../../inc/mod/pages.php:1932
+msgid "Debug: Recent posts"
+msgstr "Debug: Mensagens recentes"
+
+#: ../../inc/mod/pages.php:1956
+msgid "Debug: SQL"
+msgstr ""
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:19
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:29
+msgid "Boards"
+msgstr "Boards"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:57
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:183
+msgid "edit"
+msgstr "editar"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:74
+msgid "Create new board"
+msgstr "Criar nova board"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:84
+msgid "Messages"
+msgstr "Mensagens"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:120
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:98
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:125
+msgid "no subject"
+msgstr "sem assunto"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:161
+msgid "View all noticeboard entries"
+msgstr "Ver todas as noticias do quadro de noticias"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:192
+msgid "Administration"
+msgstr "Administração"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:239
+msgid "Change password"
+msgstr "Mudar senha"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:271
+msgid "Configuration"
+msgstr "Configuração"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:282
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:293
+msgid "Search"
+msgstr "Procurar"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:289
+msgid "Phrase:"
+msgstr "Frase:"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:297
+msgid ""
+"(Search is case-insensitive, and based on keywords. To match exact phrases, "
+"use \"quotes\". Use an asterisk (*) for wildcard.)"
+msgstr ""
+"(A procura não diferencia maiúsculas de minusculas, e baseia-se em palavras chave. para procurar por frases exatas, "
+"use \"quotes\". Utilize um asterisco (*) como coringa.)"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:309
+msgid "Debug"
+msgstr "Debug"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:313
+msgid "Anti-spam"
+msgstr "Anti-spam"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:316
+msgid "Recent posts"
+msgstr "Mensagens recentes"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:322
+msgid "SQL"
+msgstr "SQL"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:360
+msgid "User account"
+msgstr "Conta de usuário"
+
+#: ../../templates/cache/3a/df/ab38a77244cb9c729b4c6f99759a.php:365
+msgid "Logout"
+msgstr "Sair"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:21
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:21
+msgid "New post"
+msgstr "Nova mensagem"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:27
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:31
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:36
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:27
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:55
+msgid "Name"
+msgstr "Nome"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:36
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:63
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:53
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:98
+msgid "Subject"
+msgstr "Assunto"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:42
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:69
+msgid "Body"
+msgstr "Mensagem"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:49
+msgid "Post to noticeboard"
+msgstr "Postar no quadro de notícias"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:73
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:100
+msgid "delete"
+msgstr "deletar"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:106
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:133
+msgid "by"
+msgstr "por"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:118
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:112
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:344
+msgid "deleted?"
+msgstr "deletado?"
+
+#: ../../templates/cache/26/6f/05ca0da8ac09e2c2216cba2b6f95.php:125
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:136
+msgid "at"
+msgstr "em"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:74
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:169
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:331
+msgid "Staff"
+msgstr "Equipe"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:77
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:179
+msgid "Note"
+msgstr "Nota"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:80
+msgid "Date"
+msgstr "Data"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:86
+msgid "Actions"
+msgstr "Ações"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:142
+msgid "remove"
+msgstr "remover"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:189
+msgid "New note"
+msgstr "Nova nota"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:226
+msgid "Status"
+msgstr "Situação"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:233
+msgid "Expired"
+msgstr "Expirado"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:238
+msgid "Active"
+msgstr "Ativo"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:256
+#: ../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:32
+#: ../../templates/cache/9c/7b/891291bc84f8844c30cefdb949cf.php:30
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:90
+msgid "Reason"
+msgstr "Razão"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:269
+msgid "no reason"
+msgstr "sem razão especificada"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:278
+#: ../../templates/cache/9c/7b/891291bc84f8844c30cefdb949cf.php:20
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:142
+msgid "Board"
+msgstr "Board"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:291
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:150
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:83
+msgid "all boards"
+msgstr "todas as boards"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:300
+msgid "Set"
+msgstr "Configurar"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:309
+msgid "Expires"
+msgstr "Expira em"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:322
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:137
+msgid "never"
+msgstr "nunca"
+
+#: ../../templates/cache/24/a0/f1ddafed7a8f9625e747a5ca33f5.php:357
+msgid "Remove ban"
+msgstr "Remover expulsão"
+
+#: ../../templates/cache/72/55/0d64283f30702de83ecfcb71f86a.php:25
+msgid "There are no reports."
+msgstr "Não há denúncias no momento."
+
+#: ../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:19
+msgid "Delete Post"
+msgstr "Deletar Mensagem"
+
+#: ../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:22
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:218
+msgid "File"
+msgstr "Arquivo"
+
+#: ../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:23
+#: ../../templates/cache/04/54/656aa217f895c90eae78024fa060.php:41
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:310
+msgid "Password"
+msgstr "Senha"
+
+#: ../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:27
+msgid "Delete"
+msgstr "Deletar"
+
+#: ../../templates/cache/82/40/4c4a4b82f787181e6500ce83494d.php:36
+msgid "Report"
+msgstr "Denunciar"
+
+#: ../../templates/cache/04/54/656aa217f895c90eae78024fa060.php:28
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:23
+msgid "Username"
+msgstr "Usuário"
+
+#: ../../templates/cache/04/54/656aa217f895c90eae78024fa060.php:52
+msgid "Continue"
+msgstr "Prosseguir"
+
+#: ../../templates/cache/f5/e3/343716327c6183713f70a3fb57f1.php:94
+#: ../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:175
+#: ../../templates/cache/62/8c/21348d46377c3e1b3f8c476ba376.php:63
+msgid "Return to dashboard"
+msgstr "Voltar à dashboard"
+
+#: ../../templates/cache/9c/7b/891291bc84f8844c30cefdb949cf.php:36
+msgid "Report date"
+msgstr "Data da denúncia"
+
+#: ../../templates/cache/9c/7b/891291bc84f8844c30cefdb949cf.php:45
+msgid "Reported by"
+msgstr "Denunciado por"
+
+#: ../../templates/cache/9c/7b/891291bc84f8844c30cefdb949cf.php:63
+msgid "Discard abuse report"
+msgstr "Descartar denúncia desnecessária"
+
+#: ../../templates/cache/9c/7b/891291bc84f8844c30cefdb949cf.php:80
+msgid "Discard all abuse reports by this IP address"
+msgstr "Descartar todas denúncias desnecessárias deste IP"
+
+#: ../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:183
+msgid "Posting mode: Reply"
+msgstr "Modo de postagem: Resposta"
+
+#: ../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:186
+#: ../../templates/cache/aa/f6/f10fd83961bcd8c947af6ddf919d.php:232
+msgid "Return"
+msgstr "Voltar"
+
+#: ../../templates/cache/c8/8b/242bf87b3b6a29a67cdd09a3afeb.php:76
+msgid "Post news entry"
+msgstr "Postar nova notícia"
+
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:66
+msgid "(or subnet)"
+msgstr "(ou subnet)"
+
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:80
+msgid "hidden"
+msgstr "oculto"
+
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:107
+msgid "Message"
+msgstr "Mensagem"
+
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:117
+msgid "public; attached to post"
+msgstr "público; anexado à mensagem"
+
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:133
+msgid "Length"
+msgstr "Tamanho"
+
+#: ../../templates/cache/18/9c/c365d711719f494c684aab98a4ae.php:192
+msgid "New Ban"
+msgstr "Nova Expulsão"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:20
+msgid "ID"
+msgstr "ID"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:26
+msgid "Type"
+msgstr "Tipo"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:35
+msgid "Last action"
+msgstr "Ultima ação"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:61
+msgid "Janitor"
+msgstr "Faxineiro"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:64
+msgid "Mod"
+msgstr "Moderador"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:67
+msgid "Admin"
+msgstr "Administrador"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:78
+msgid "none"
+msgstr "nenhum"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:153
+msgid "Promote"
+msgstr "Promover"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:163
+msgid "Demote"
+msgstr "Rebaixar"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:173
+msgid "log"
+msgstr "registro"
+
+#: ../../templates/cache/c5/a7/fac83da087ee6e24edaf09e01122.php:193
+msgid "PM"
+msgstr "MP"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:105
+msgid "File:"
+msgstr "Arquivo:"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:117
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:129
+msgid "Spoiler Image"
+msgstr "Imagem Spoiler"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:463
+msgid "Reply"
+msgstr "Responder"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:490
+msgid "1 post"
+msgid_plural "%count% posts"
+msgstr[0] "1 mensagem"
+msgstr[1] "%count% mensagens"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:496
+msgid "and"
+msgstr "e"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:507
+msgid "1 image reply"
+msgid_plural "%count% image replies"
+msgstr[0] "1 resposta com imagem"
+msgstr[1] "%count% respostas com imagem"
+
+#: ../../templates/cache/d8/f2/7780eb1adcdbda7e332659e3fb4f.php:512
+msgid "omitted. Click reply to view."
+msgstr "omitidas. Clique em responder para visualizar."
+
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:40
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:76
+msgid "Email"
+msgstr "E-mail"
+
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:62
+msgid "Update"
+msgstr "Atualizar"
+
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:69
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:138
+msgid "Comment"
+msgstr "Comentar"
+
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:89
+msgid "Currently editing raw HTML."
+msgstr "Editando em HTML puro."
+
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:96
+msgid "Edit markup instead?"
+msgstr "Editar markup em vez disso?"
+
+#: ../../templates/cache/39/42/cbc36382096edfa72a8bc26e4514.php:105
+msgid "Edit raw HTML instead?"
+msgstr "Editar em HTML puro em vez disso?"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:111
+msgid "Submit"
+msgstr "Enviar"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:159
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:185
+msgid "Verification"
+msgstr "Verification"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:236
+msgid "Embed"
+msgstr "Inserir"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:259
+msgid "Flags"
+msgstr "Sinalizações"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:268
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:271
+msgid "Sticky"
+msgstr "Fixar"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:280
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:283
+msgid "Lock"
+msgstr "Trancar"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:292
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:295
+msgid "Raw HTML"
+msgstr "HTML Puro"
+
+#: ../../templates/cache/0c/37/9331df01df7c2986d77a02d3beb0.php:319
+msgid "(For file deletion.)"
+msgstr "(Para excluir arquivos)"
diff --git a/inc/mod/auth.php b/inc/mod/auth.php
index 8fb01ecb..173190ab 100644
--- a/inc/mod/auth.php
+++ b/inc/mod/auth.php
@@ -98,8 +98,10 @@ if (isset($_COOKIE[$config['cookies']['mod']])) {
// Should be username:hash:salt
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]);
if (count($cookie) != 3) {
+ // Malformed cookies
destroyCookies();
- error($config['error']['malformed']);
+ mod_login();
+ exit;
}
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM `mods` WHERE `username` = :username LIMIT 1");
@@ -111,7 +113,8 @@ if (isset($_COOKIE[$config['cookies']['mod']])) {
if ($cookie[1] !== mkhash($cookie[0], $user['password'], $cookie[2])) {
// Malformed cookies
destroyCookies();
- error($config['error']['malformed']);
+ mod_login();
+ exit;
}
$mod = array(
@@ -125,7 +128,7 @@ if (isset($_COOKIE[$config['cookies']['mod']])) {
function create_pm_header() {
global $mod, $config;
- if ($config['cache']['enabled'] && ($header = cache::get('pm_unread_' . $mod['id'])) !== false) {
+ if ($config['cache']['enabled'] && ($header = cache::get('pm_unread_' . $mod['id'])) != false) {
if ($header === true)
return false;
diff --git a/inc/mod/ban.php b/inc/mod/ban.php
index 9c93d70e..cfc2636f 100644
--- a/inc/mod/ban.php
+++ b/inc/mod/ban.php
@@ -56,7 +56,7 @@ function parse_time($str) {
function ban($mask, $reason, $length, $board) {
global $mod, $pdo;
- $query = prepare("INSERT INTO `bans` VALUES (NULL, :ip, :mod, :time, :expires, :reason, :board)");
+ $query = prepare("INSERT INTO `bans` VALUES (NULL, :ip, :mod, :time, :expires, :reason, :board, 0)");
$query->bindValue(':ip', $mask);
$query->bindValue(':mod', $mod['id']);
$query->bindValue(':time', time());
@@ -80,16 +80,25 @@ function ban($mask, $reason, $length, $board) {
modLog('Created a new ' .
($length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent') .
- ' ban (<small>#' . $pdo->lastInsertId() . '</small>) for ' .
+ ' ban on ' .
+ ($board ? '/' . $board . '/' : 'all boards') .
+ ' for ' .
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : utf8tohtml($mask)) .
+ ' (<small>#' . $pdo->lastInsertId() . '</small>)' .
' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason'));
}
-function unban($id) {
+function unban($id) {
+ $query = prepare("SELECT `ip` FROM `bans` WHERE `id` = :id");
+ $query->bindValue(':id', $id);
+ $query->execute() or error(db_error($query));
+ $mask = $query->fetchColumn();
+
$query = prepare("DELETE FROM `bans` WHERE `id` = :id");
$query->bindValue(':id', $id);
$query->execute() or error(db_error($query));
- modLog("Removed ban #{$id}");
+ if ($mask)
+ modLog("Removed ban #{$id} for " . (filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : utf8tohtml($mask)));
}
diff --git a/inc/mod/pages.php b/inc/mod/pages.php
index 9ccdcb77..12ad1e92 100644
--- a/inc/mod/pages.php
+++ b/inc/mod/pages.php
@@ -92,7 +92,7 @@ function mod_dashboard() {
}
}
- if (!$config['cache']['enabled'] || ($args['unread_pms'] = cache::get('pm_unreadcount_' . $mod['id'])) === false) {
+ if (!$config['cache']['enabled'] || ($args['unread_pms'] = cache::get('pm_unreadcount_' . $mod['id'])) == false) {
$query = prepare('SELECT COUNT(*) FROM `pms` WHERE `to` = :id AND `unread` = 1');
$query->bindValue(':id', $mod['id']);
$query->execute() or error(db_error($query));
@@ -114,26 +114,37 @@ function mod_dashboard() {
} else {
$ctx = stream_context_create(array('http' => array('timeout' => 5)));
if ($code = @file_get_contents('http://tinyboard.org/version.txt', 0, $ctx)) {
- eval($code);
- if (preg_match('/v(\d+)\.(\d)\.(\d+)(-dev.+)?$/', $config['version'], $matches)) {
- $current = array(
- 'massive' => (int) $matches[1],
- 'major' => (int) $matches[2],
- 'minor' => (int) $matches[3]
+ $ver = strtok($code, "\n");
+
+ if (preg_match('@^// v(\d+)\.(\d+)\.(\d+)\s*?$@', $ver, $matches)) {
+ $latest = array(
+ 'massive' => $matches[1],
+ 'major' => $matches[2],
+ 'minor' => $matches[3]
);
- if (isset($m[4])) {
- // Development versions are always ahead in the versioning numbers
- $current['minor'] --;
- }
- // Check if it's newer
- if (!( $latest['massive'] > $current['massive'] ||
- $latest['major'] > $current['major'] ||
- ($latest['massive'] == $current['massive'] &&
- $latest['major'] == $current['major'] &&
- $latest['minor'] > $current['minor']
- )))
+ if (preg_match('/v(\d+)\.(\d)\.(\d+)(-dev.+)?$/', $config['version'], $matches)) {
+ $current = array(
+ 'massive' => (int) $matches[1],
+ 'major' => (int) $matches[2],
+ 'minor' => (int) $matches[3]
+ );
+ if (isset($m[4])) {
+ // Development versions are always ahead in the versioning numbers
+ $current['minor'] --;
+ }
+ // Check if it's newer
+ if (!( $latest['massive'] > $current['massive'] ||
+ $latest['major'] > $current['major'] ||
+ ($latest['massive'] == $current['massive'] &&
+ $latest['major'] == $current['major'] &&
+ $latest['minor'] > $current['minor']
+ )))
+ $latest = false;
+ } else {
$latest = false;
+ }
} else {
+ // Couldn't get latest version
$latest = false;
}
} else {
@@ -206,6 +217,19 @@ function mod_edit_board($boardName) {
$query = prepare('DELETE FROM `antispam` WHERE `board` = :board');
$query->bindValue(':board', $board['uri']);
$query->execute() or error(db_error($query));
+
+ // Remove board from users/permissions table
+ $query = query('SELECT `id`,`boards` FROM `mods`') or error(db_error());
+ while ($user = $query->fetch(PDO::FETCH_ASSOC)) {
+ $user_boards = explode(',', $user['boards']);
+ if (in_array($board['uri'], $user_boards)) {
+ unset($user_boards[array_search($board['uri'], $user_boards)]);
+ $_query = prepare('UPDATE `mods` SET `boards` = :boards WHERE `id` = :id');
+ $_query->bindValue(':boards', implode(',', $user_boards));
+ $_query->bindValue(':id', $user['id']);
+ $_query->execute() or error(db_error($_query));
+ }
+ }
} else {
$query = prepare('UPDATE `boards` SET `title` = :title, `subtitle` = :subtitle WHERE `uri` = :uri');
$query->bindValue(':uri', $board['uri']);
@@ -589,6 +613,15 @@ function mod_page_ip($ip) {
$args['notes'] = $query->fetchAll(PDO::FETCH_ASSOC);
}
+ if (hasPermission($config['mod']['modlog_ip'])) {
+ $query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM `modlogs` LEFT JOIN `mods` ON `mod` = `mods`.`id` WHERE `text` LIKE :search ORDER BY `time` DESC LIMIT 20");
+ $query->bindValue(':search', '%' . $ip . '%');
+ $query->execute() or error(db_error($query));
+ $args['logs'] = $query->fetchAll(PDO::FETCH_ASSOC);
+ } else {
+ $args['logs'] = array();
+ }
+
mod_page(sprintf('%s: %s', _('IP'), $ip), 'mod/view_ip.html', $args, $args['hostname']);
}
@@ -631,7 +664,8 @@ function mod_bans($page_no = 1) {
if (preg_match('/^ban_(\d+)$/', $name, $match))
$unban[] = $match[1];
}
-
+ if (isset($config['mod']['unban_limit'])){
+ if (count($unban) <= $config['mod']['unban_limit'] || $config['mod']['unban_limit'] == -1){
if (!empty($unban)) {
query('DELETE FROM `bans` WHERE `id` = ' . implode(' OR `id` = ', $unban)) or error(db_error());
@@ -639,7 +673,21 @@ function mod_bans($page_no = 1) {
modLog("Removed ban #{$id}");
}
}
+ } else {
+ error(sprintf($config['error']['toomanyunban'], $config['mod']['unban_limit'], count($unban) ));
+ }
+ } else {
+
+ if (!empty($unban)) {
+ query('DELETE FROM `bans` WHERE `id` = ' . implode(' OR `id` = ', $unban)) or error(db_error());
+
+ foreach ($unban as $id) {
+ modLog("Removed ban #{$id}");
+ }
+ }
+
+ }
header('Location: ?/bans', true, $config['redirect_http']);
}
@@ -690,6 +738,13 @@ function mod_lock($board, $unlock, $post) {
buildIndex();
}
+ if ($config['mod']['dismiss_reports_on_lock']) {
+ $query = prepare('DELETE FROM `reports` WHERE `board` = :board AND `post` = :id');
+ $query->bindValue(':board', $board);
+ $query->bindValue(':id', $post);
+ $query->execute() or error(db_error($query));
+ }
+
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
if ($unlock)
@@ -871,8 +926,10 @@ function mod_move($originBoard, $postID) {
modLog("Moved thread #${postID} to " . sprintf($config['board_abbreviation'], $targetBoard) . " (#${newID})", $originBoard);
- // build new hread
+ // build new thread
buildThread($newID);
+
+ clean();
buildIndex();
// trigger themes
@@ -892,7 +949,7 @@ function mod_move($originBoard, $postID) {
'mod' => true,
'subject' => '',
'email' => '',
- 'name' => $config['mod']['shadow_name'],
+ 'name' => (!$config['mod']['shadow_name'] ? $config['anonymous'] : $config['mod']['shadow_name']),
'capcode' => $config['mod']['shadow_capcode'],
'trip' => '',
'password' => '',
@@ -1834,6 +1891,7 @@ function mod_theme_configure($theme_name) {
'result' => $result,
'message' => $message,
));
+ return;
}
$settings = themeSettings($theme_name);
diff --git a/install.php b/install.php
index 1447c723..42fbb15d 100644
--- a/install.php
+++ b/install.php
@@ -1,7 +1,7 @@
<?php
// Installation/upgrade file
-define('VERSION', 'v0.9.6-dev-7');
+define('VERSION', 'v0.9.6-dev-8 + <a href="https://github.com/vichan-devel/Tinyboard/">vichan-devel-4.0.1</a>');
require 'inc/functions.php';
@@ -227,6 +227,9 @@ if (file_exists($config['has_installed'])) {
query(sprintf("CREATE INDEX `thread_id` ON `posts_%s` (`thread`, `id`)", $_board['uri'])) or error(db_error());
query(sprintf("ALTER TABLE `posts_%s` DROP INDEX `thread`", $_board['uri'])) or error(db_error());
}
+ case 'v0.9.6-dev-7':
+ case 'v0.9.6-dev-7 + <a href="https://github.com/vichan-devel/Tinyboard/">vichan-devel-4.0-gold</a>':
+ query("ALTER TABLE `bans` ADD `seen` BOOLEAN NOT NULL") or error(db_error());
case false:
// Update version number
file_write($config['has_installed'], VERSION);
diff --git a/install.sql b/install.sql
index 811811e2..04896681 100644
--- a/install.sql
+++ b/install.sql
@@ -51,6 +51,7 @@ CREATE TABLE IF NOT EXISTS `bans` (
`expires` int(11) DEFAULT NULL,
`reason` text,
`board` varchar(120) DEFAULT NULL,
+ `seen` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `ip` (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
diff --git a/js/auto-reload.js b/js/auto-reload.js
index c0c63056..5d96146a 100644
--- a/js/auto-reload.js
+++ b/js/auto-reload.js
@@ -16,6 +16,9 @@
$(document).ready(function(){
if($('div.banner').length == 0)
return; // not index
+
+ if($(".post.op").size() != 1)
+ return; //not thread page
var poll_interval;
diff --git a/js/expand.js b/js/expand.js
index 15408813..6371f92c 100644
--- a/js/expand.js
+++ b/js/expand.js
@@ -1,5 +1,5 @@
/*
- *expand.js
+ * expand.js
* https://github.com/savetheinternet/Tinyboard/blob/master/js/expand.js
*
* Released under the MIT license
@@ -17,7 +17,7 @@ $(document).ready(function(){
$('div.post.op span.omitted').each(function() {
$(this)
- .html($(this).text().replace(/Click reply to view\.|Kliknij Odpowiedź aby zobaczyć\./, '<a href="javascript:void(0)">Click to expand</a>.'))
+ .html($(this).text().replace(_("Click reply to view."), '<a href="javascript:void(0)">'+_("Click to expand")+'</a>.'))
.find('a').click(function() {
var thread = $(this).parent().parent().parent();
var id = thread.attr('id').replace(/^thread_/, '');
@@ -35,9 +35,10 @@ $(document).ready(function(){
}
last_expanded = $(this);
+ $(document).trigger('new_post', this);
}
});
- $('<span class="omitted"><a href="javascript:void(0)">Hide expanded replies</a>.</span>')
+ $('<span class="omitted"><a href="javascript:void(0)">' + _('Hide expanded replies') + '</a>.</span>')
.insertAfter(thread.find('span.omitted').css('display', 'none'))
.click(function() {
thread.find('.expanded').remove();
diff --git a/js/forced-anon.js b/js/forced-anon.js
index 3dbf0dfc..6ae43882 100644
--- a/js/forced-anon.js
+++ b/js/forced-anon.js
@@ -56,17 +56,17 @@ $(document).ready(function() {
forced_anon = localStorage['forcedanon'] ? true : false;
$('hr:first').before('<div id="forced-anon" style="text-align:right"><a class="unimportant" href="javascript:void(0)">-</a></div>');
- $('div#forced-anon a').text('Forced anonymity (' + (forced_anon ? 'enabled' : 'disabled') + ')');
+ $('div#forced-anon a').text(_('Forced anonymity')+' (' + (forced_anon ? _('enabled') : _('disabled')) + ')');
$('div#forced-anon a').click(function() {
forced_anon = !forced_anon;
if(forced_anon) {
- $('div#forced-anon a').text('Forced anonymity (enabled)');
+ $('div#forced-anon a').text(_('Forced anonymity')+' ('+_('enabled')+')');
localStorage.forcedanon = true;
enable_fa();
} else {
- $('div#forced-anon a').text('Forced anonymity (disabled)');
+ $('div#forced-anon a').text(_('Forced anonymity')+' ('+_('disabled')+')');
delete localStorage.forcedanon;
disable_fa();
}
diff --git a/js/inline-expanding.js b/js/inline-expanding.js
index f6714d7f..7c9923fa 100644
--- a/js/inline-expanding.js
+++ b/js/inline-expanding.js
@@ -6,42 +6,50 @@
* Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org>
*
* Usage:
+ * $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/inline-expanding.js';
*
*/
onready(function(){
- var link = document.getElementsByTagName('a');
+ var inline_expand_post = function() {
+ var link = this.getElementsByTagName('a');
- for(var i = 0; i < link.length; i++) {
- if(typeof link[i] == "object" && link[i].childNodes[0].src && link[i].className != 'file') {
- link[i].childNodes[0].style.maxWidth = '95%';
- link[i].childNodes[0].style.maxHeight = '95%';
- link[i].onclick = function(e) {
- if(e.which == 2) {
- return true;
- }
- if(!this.tag) {
- this.tag = this.childNodes[0].src;
- this.childNodes[0].src = this.href;
- this.childNodes[0].style.width = 'auto';
- this.childNodes[0].style.height = 'auto';
- this.childNodes[0].style.opacity = '0.4';
- this.childNodes[0].style.filter = 'alpha(opacity=40)';
- this.childNodes[0].onload = function() {
- this.style.opacity = '1';
- this.style.filter = '';
+ for(var i = 0; i < link.length; i++) {
+ if(typeof link[i] == "object" && typeof link[i].childNodes[0] !== 'undefined' && link[i].childNodes[0].src && link[i].className != 'file') {
+ link[i].childNodes[0].style.maxWidth = '95%';
+ link[i].onclick = function(e) {
+ if(e.which == 2) {
+ return true;
+ }
+ if(!this.tag) {
+ this.tag = this.childNodes[0].src;
+ this.childNodes[0].src = this.href;
+ this.childNodes[0].style.width = 'auto';
+ this.childNodes[0].style.height = 'auto';
+ this.childNodes[0].style.opacity = '0.4';
+ this.childNodes[0].style.filter = 'alpha(opacity=40)';
+ this.childNodes[0].onload = function() {
+ this.style.opacity = '1';
+ this.style.filter = '';
+ }
+ } else {
+ this.childNodes[0].src = this.tag;
+ this.childNodes[0].style.width = 'auto';
+ this.childNodes[0].style.height = 'auto';
+ this.tag = '';
}
- } else {
- this.childNodes[0].src = this.tag;
- this.childNodes[0].style.width = 'auto';
- this.childNodes[0].style.height = 'auto';
- this.tag = '';
+ return false;
}
- return false;
}
-
}
}
+
+ $('div[id^="thread_"]').each(inline_expand_post);
+
+ // allow to work with auto-reload.js, etc.
+ $(document).bind('new_post', function(e, post) {
+ inline_expand_post.call(post);
+ });
});
diff --git a/js/local-time.js b/js/local-time.js
index b92cc5be..a4e2fb51 100644
--- a/js/local-time.js
+++ b/js/local-time.js
@@ -6,6 +6,7 @@
* Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org>
*
* Usage:
+ * $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/local-time.js';
*
*/
@@ -21,23 +22,32 @@ onready(function(){
var zeropad = function(num, count) {
return [Math.pow(10, count - num.toString().length), num].join('').substr(1);
};
+
+ var do_localtime = function(elem) {
+ var times = elem.getElementsByTagName('time');
- var times = document.getElementsByTagName('time');
-
- for(var i = 0; i < times.length; i++) {
- if(!times[i].innerHTML.match(/^\d+\/\d+\/\d+ \(\w+\) \d+:\d+:\d+$/))
- continue;
-
- var t = iso8601(times[i].getAttribute('datetime'));
+ for(var i = 0; i < times.length; i++) {
+ if(typeof times[i].getAttribute('data-local') == 'undefined')
+ continue;
+ var t = iso8601(times[i].getAttribute('datetime'));
- times[i].innerHTML =
- // date
- zeropad(t.getMonth() + 1, 2) + "/" + zeropad(t.getDate(), 2) + "/" + t.getFullYear().toString().substring(2) +
- " (" + ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][t.getDay()] + ") " +
- // time
- zeropad(t.getHours(), 2) + ":" + zeropad(t.getMinutes(), 2) + ":" + zeropad(t.getSeconds(), 2);
+ times[i].setAttribute('data-local', 'true');
+ times[i].innerHTML =
+ // date
+ zeropad(t.getMonth() + 1, 2) + "/" + zeropad(t.getDate(), 2) + "/" + t.getFullYear().toString().substring(2) +
+ " (" + [_("Sun"), _("Mon"), _("Tue"), _("Wed"), _("Thu"), _("Fri"), _("Sat"), _("Sun")][t.getDay()] + ") " +
+ // time
+ zeropad(t.getHours(), 2) + ":" + zeropad(t.getMinutes(), 2) + ":" + zeropad(t.getSeconds(), 2);
+ };
};
+
+ do_localtime(document);
+
+ // allow to work with auto-reload.js, etc.
+ $(document).bind('new_post', function(e, post) {
+ do_localtime(post);
+ });
});
diff --git a/js/post-hider.js b/js/post-hider.js
index 5cb740a5..742c21bc 100644
--- a/js/post-hider.js
+++ b/js/post-hider.js
@@ -1,45 +1,53 @@
-function phGetCookieName(id) {
- return "ph_hide_" + id;
+function phGetCookieName(board, id) {
+ return "ph_hide_" + board + "_" + id;
}
-function phPostHidden(id) {
- return (localStorage.getItem(phGetCookieName(id)) != null);
+function phPostHidden(board, id) {
+ return (localStorage.getItem(phGetCookieName(board, id)) != null);
}
-function phPostToggle(id) {
- if(phPostHidden(id)) { localStorage.removeItem(phGetCookieName(id)); }
- else { localStorage.setItem(phGetCookieName(id),"yes"); }
+function phPostToggle(board, id) {
+ if(phPostHidden(board, id)) { localStorage.removeItem(phGetCookieName(board, id)); }
+ else { localStorage.setItem(phGetCookieName(board, id),"yes"); }
}
-function phGetInnerText(id) {
- if(phPostHidden(id)) { return "[+]"; }
- else { return "[-]"; }
+function phGetInnerText(board, id) {
+ if(phPostHidden(board, id)) { return "[+]"; }
+ else { return "[–]"; }
}
function phGetOpID(element) {
return Number(element.children("div.post.op").children("p.intro").children("a.post_no.p2").text());
}
+function phGetOpBoard(element) {
+ return element.data("board");
+}
function phPostHandle(element) {
var id = phGetOpID(element);
+ var board = phGetOpBoard(element);
var preplies = element.children("div.post.reply");
var pbody = element.children("div.post.op").children("div.body");
var pimage = element.children("a:first").children("img");
var pbutton = element.children("div.post.op").children("p.intro").children("a.posthider");
var pomitted = element.children("div.post.op").children("span.omitted");
- if(phPostHidden(id)) { element.addClass("thread-hidden"); pomitted.hide(); preplies.hide(); pbody.hide(); pimage.hide(); pbutton.text("[+]"); }
- else { element.removeClass("thread-hidden"); pomitted.show(); preplies.show(); pbody.show(); pimage.show(); pbutton.text("[-]"); }
+ if(phPostHidden(board, id)) { element.addClass("thread-hidden"); pomitted.hide(); preplies.hide(); pbody.hide(); pimage.hide(); pbutton.text("[+]"); }
+ else { element.removeClass("thread-hidden"); pomitted.show(); preplies.show(); pbody.show(); pimage.show(); pbutton.text("[–]"); }
+}
+
+function phHandleThread(index, element) {
+ // Get thread ID.
+ var pin = $(this).children("div.post.op").children("p.intro");
+ var tid = phGetOpID($(this));
+ if(tid != NaN) {
+ $("<a href='javascript:;' class='posthider'>[?]</a>").insertAfter(pin.children('a:last')).click(function(e) {
+ var eO = $(e.target);
+ var par = eO.parent().parent().parent();
+ phPostToggle(phGetOpBoard(par), phGetOpID(par));
+ phPostHandle(par);
+ return false;
+ });
+ phPostHandle($(this));
+ }
}
$(document).ready(function(){
- $('div[id^="thread"]').each(function(index, element){
- // Get thread ID.
- var pin = $(this).children("div.post.op").children("p.intro");
- var tid = phGetOpID($(this));
- if(tid != NaN) {
- $("<a href='javascript:;' class='posthider'>[?]</a>").insertAfter(pin.children('a:last')).click(function(e) {
- var eO = $(e.target);
- var par = eO.parent().parent().parent();
- phPostToggle(phGetOpID(par));
- phPostHandle(par);
- return false;
- });
- phPostHandle($(this));
- }
- });
+ if (active_page != "thread") {
+ $('form[name="postcontrols"] > div[id^="thread"]').each(phHandleThread);
+ }
});
diff --git a/js/post-hover.js b/js/post-hover.js
index 2f25540b..48f0ed96 100644
--- a/js/post-hover.js
+++ b/js/post-hover.js
@@ -20,6 +20,8 @@ onready(function(){
if(id = $link.text().match(/^>>(\d+)$/)) {
id = id[1];
+ } else {
+ return;
}
var $post = false;
diff --git a/js/quick-post-controls.js b/js/quick-post-controls.js
index 0c164bbb..c28f874f 100644
--- a/js/quick-post-controls.js
+++ b/js/quick-post-controls.js
@@ -24,17 +24,17 @@ $(document).ready(function(){
'<input type="hidden" name="delete_' + id + '">' +
- '<label for="password_' + id + '">Password</label>: ' +
+ '<label for="password_' + id + '">'+_("Password")+'</label>: ' +
'<input id="password_' + id + '" type="password" name="password" size="11" maxlength="18">' +
- '<input title="Delete file only" type="checkbox" name="file" id="delete_file_' + id + '">' +
- '<label for="delete_file_' + id + '">File</label>' +
- ' <input type="submit" name="delete" value="Delete">' +
+ '<input title="'+_('Delete file only')+'" type="checkbox" name="file" id="delete_file_' + id + '">' +
+ '<label for="delete_file_' + id + '">'+_('File')+'</label>' +
+ ' <input type="submit" name="delete" value="'+_('Delete')+'">' +
'<br>' +
- '<label for="reason_' + id + '">Reason</label>: ' +
+ '<label for="reason_' + id + '">'+_('Reason')+'</label>: ' +
'<input id="reason_' + id + '" type="text" name="reason" size="20" maxlength="100">' +
- ' <input type="submit" name="report" value="Report">' +
+ ' <input type="submit" name="report" value="'+_('Report')+'">' +
'</div>' +
'</form>');
post_form
diff --git a/js/quick-reply.js b/js/quick-reply.js
index ee59e805..bee204b7 100644
--- a/js/quick-reply.js
+++ b/js/quick-reply.js
@@ -12,12 +12,13 @@
*
*/
+if (active_page == 'index') {
$(document).ready(function(){
if($('div.banner').length != 0)
return; // not index
txt_new_topic = $('form[name=post] input[type=submit]').val();
- txt_new_reply = txt_new_topic == 'Submit' ? txt_new_topic : 'Reply';
+ txt_new_reply = txt_new_topic == _('Submit') ? txt_new_topic : new_reply_string;
undo_quick_reply = function() {
$('div.banner').remove();
@@ -26,10 +27,10 @@ $(document).ready(function(){
}
$('div.post.op').each(function() {
- var id = $(this).children('p.intro').children('a.post_no:eq(2)').text();
- $('<a href="#">[Quick Reply]</a>').insertAfter($(this).children('p.intro').children('a:last')).click(function() {
+ var id = $(this).children('p.intro').children('a.post_no:eq(1)').text();
+ $('<a href="#">['+_("Quick reply")+']</a>').insertAfter($(this).children('p.intro').children('a:last')).click(function() {
$('div.banner').remove();
- $('<div class="banner">Post Mode: Quick Reply to <small>&gt;&gt;' + id + '</small> <a class="unimportant" onclick="undo_quick_reply()" href="javascript:void(0)">[Return]</a></div>')
+ $('<div class="banner">'+fmt(_("Posting mode: Replying to <small>&gt;&gt;{0}</small>"), [id])+' <a class="unimportant" onclick="undo_quick_reply()" href="javascript:void(0)">['+_("Return")+']</a></div>')
.insertBefore('form[name=post]');
$('form[name=post] input[type=submit]').val(txt_new_reply);
@@ -43,4 +44,4 @@ $(document).ready(function(){
});
});
});
-
+}
diff --git a/js/show-backlinks.js b/js/show-backlinks.js
index e6e17e4f..4ead494b 100644
--- a/js/show-backlinks.js
+++ b/js/show-backlinks.js
@@ -46,5 +46,9 @@ onready(function(){
};
$('div.post.reply').each(showBackLinks);
+
+ $(document).bind('new_post', function(e, post) {
+ showBackLinks.call(post);
+ });
});
diff --git a/js/smartphone-spoiler.js b/js/smartphone-spoiler.js
index 67b9c789..ee904b54 100644
--- a/js/smartphone-spoiler.js
+++ b/js/smartphone-spoiler.js
@@ -13,12 +13,21 @@
onready(function(){
if(device_type == 'mobile') {
- var spoilers = document.getElementsByClassName('spoiler');
- for(var i = 0; i < spoilers.length; i++) {
- spoilers[i].onmousedown = function() {
- this.style.color = 'white';
- };
- }
+ var fix_spoilers = function(where) {
+ var spoilers = where.getElementsByClassName('spoiler');
+ for(var i = 0; i < spoilers.length; i++) {
+ spoilers[i].onmousedown = function() {
+ this.style.color = 'white';
+ };
+ }
+ };
+ fix_spoilers(document);
+
+ // allow to work with auto-reload.js, etc.
+ $(document).bind('new_post', function(e, post) {
+ fix_spoilers(post);
+ });
+
}
});
diff --git a/js/toggle-locked-threads.js b/js/toggle-locked-threads.js
index 50c44eda..1d517a4d 100644
--- a/js/toggle-locked-threads.js
+++ b/js/toggle-locked-threads.js
@@ -36,7 +36,7 @@ $(document).ready(function(){
$('hr:first').before('<div id="toggle-locked-threads" style="text-align:right"><a class="unimportant" href="javascript:void(0)">-</a></div>');
$('div#toggle-locked-threads a')
- .text((hide_locked_threads ? 'Show' : 'Hide') + ' locked threads')
+ .text(hide_locked_threads ? _('Show locked threads') : _('Hide locked threads'))
.click(function() {
hide_locked_threads = !hide_locked_threads;
if (hide_locked_threads) {
@@ -51,7 +51,7 @@ $(document).ready(function(){
delete localStorage.hidelockedthreads;
}
- $(this).text((hide_locked_threads ? 'Show' : 'Hide') + ' locked threads')
+ $(this).text(hide_locked_threads ? _('Show locked threads') : _('Hide locked threads'))
});
if (hide_locked_threads) {
diff --git a/mod.php b/mod.php
index c0614754..e76f1f01 100644
--- a/mod.php
+++ b/mod.php
@@ -5,8 +5,8 @@
*/
require 'inc/functions.php';
-require 'inc/mod/auth.php';
require 'inc/mod/pages.php';
+require 'inc/mod/auth.php';
// Fix for magic quotes
if (get_magic_quotes_gpc()) {
@@ -45,7 +45,7 @@ $pages = array(
'/news/(\d+)' => 'news', // view news
'/news/delete/(\d+)' => 'news_delete', // delete from news
- '/edit/(\w+)' => 'edit_board', // edit board details
+ '/edit/([\w+.]+)' => 'edit_board', // edit board details
'/new-board' => 'new_board', // create a new board
'/rebuild' => 'rebuild', // rebuild static files
@@ -103,9 +103,9 @@ if (isset($config['mod']['custom_pages'])) {
$new_pages = array();
foreach ($pages as $key => $callback) {
- if (preg_match('/^secure /', $callback))
+ if (is_string($callback) && preg_match('/^secure /', $callback))
$key .= '(/(?P<token>[a-f0-9]{8}))?';
- $new_pages[@$key[0] == '!' ? $key : "!^$key$!"] = $callback;
+ $new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!'] = $callback;
}
$pages = $new_pages;
@@ -113,7 +113,7 @@ foreach ($pages as $uri => $handler) {
if (preg_match($uri, $query, $matches)) {
$matches = array_slice($matches, 1);
- if (preg_match('/^secure(_POST)? /', $handler, $m)) {
+ if (is_string($handler) && preg_match('/^secure(_POST)? /', $handler, $m)) {
$secure_post_only = isset($m[1]);
if (!$secure_post_only || $_SERVER['REQUEST_METHOD'] == 'POST') {
$token = isset($matches['token']) ? $matches['token'] : (isset($_POST['token']) ? $_POST['token'] : false);
diff --git a/post.php b/post.php
index 56091040..2825b40b 100644
--- a/post.php
+++ b/post.php
@@ -80,6 +80,7 @@ if (isset($_POST['delete'])) {
$is_mod = isset($_POST['mod']) && $_POST['mod'];
$root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
+ if (!$is_mod) header('X-Associated-Content: "' . $root . $board['dir'] . $config['file_index'] . '"');
header('Location: ' . $root . $board['dir'] . $config['file_index'], true, $config['redirect_http']);
} elseif (isset($_POST['report'])) {
@@ -137,6 +138,7 @@ if (isset($_POST['delete'])) {
$is_mod = isset($_POST['mod']) && $_POST['mod'];
$root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
+ if (!$is_mod) header('X-Associated-Content: "' . $root . $board['dir'] . $config['file_index'] . '"');
header('Location: ' . $root . $board['dir'] . $config['file_index'], true, $config['redirect_http']);
} elseif (isset($_POST['post'])) {
@@ -308,13 +310,21 @@ if (isset($_POST['delete'])) {
}
}
- // Check if thread is locked
- // but allow mods to post
- if (!$post['op'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) {
- if ($thread['locked'])
+ 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['has_file']) {
$size = $_FILES['file']['size'];
if ($size > $config['max_filesize'])
@@ -625,10 +635,6 @@ if (isset($_POST['delete'])) {
incrementSpamHash($post['antispam_hash']);
}
- if (isset($post['antispam_hash'])) {
- incrementSpamHash($post['antispam_hash']);
- }
-
if (isset($post['tracked_cites'])) {
foreach ($post['tracked_cites'] as $cite) {
$query = prepare('INSERT INTO `cites` VALUES (:board, :post, :target_board, :target)');
@@ -642,7 +648,7 @@ if (isset($_POST['delete'])) {
buildThread($post['op'] ? $id : $post['thread']);
- if (!$post['op'] && strtolower($post['email']) != 'sage' && !$thread['sage'] && ($config['reply_limit'] == 0 || numPosts($post['thread']) < $config['reply_limit'])) {
+ if (!$post['op'] && strtolower($post['email']) != 'sage' && !$thread['sage'] && ($config['reply_limit'] == 0 || $numposts['replies']+1 < $config['reply_limit'])) {
bumpThread($post['thread']);
}
@@ -679,7 +685,13 @@ if (isset($_POST['delete'])) {
_syslog(LOG_INFO, 'New post: /' . $board['dir'] . $config['dir']['res'] .
sprintf($config['file_page'], $post['op'] ? $id : $post['thread']) . (!$post['op'] ? '#' . $id : ''));
- rebuildThemes('post');
+ if (!$post['mod']) header('X-Associated-Content: "' . $redirect . '"');
+
+ if ($post['op'])
+ rebuildThemes('post-thread', $board['uri']);
+ else
+ rebuildThemes('post', $board['uri']);
+
header('Location: ' . $redirect, true, $config['redirect_http']);
} else {
if (!file_exists($config['has_installed'])) {
diff --git a/stylesheets/gentoochan.css b/stylesheets/gentoochan.css
new file mode 100644
index 00000000..8213eb26
--- /dev/null
+++ b/stylesheets/gentoochan.css
@@ -0,0 +1,67 @@
+body {
+ background: #0E0E0E url(data:image/gif;base64,R0lGODlhGAAMAKEEAOXl5ebm5vDw8PHx8SH+EUNyZWF0ZWQgd2l0aCBHSU1QACwAAAAAGAAMAAACRpQiY6cLa146MyY1EJQKjG81lNGRUPOIkgMJHtquBgIO7xwvpbrpduUSuXq8ntEC0bBEylYitdDAdM1ViaobkgKgZwyDLAAAOw==) repeat 0 0!important;
+ color: #000;
+}
+a:link, a:visited, p.intro a.email span.name {
+ -webkit-transition: all ease-in 0.3s;
+ -moz-transition: all ease-in 0.3s;
+ color: rgba(0, 0, 0, 0.6);
+ text-decoration:none !important;
+}
+a:link:hover {
+ -moz-transition: all ease-in 0.5s;
+ text-shadow: 0px 0px 2px #000;
+}
+a.post_no {
+ color: #fff;
+}
+.boardlist {
+ color: #ccc;
+}
+div.post.reply, input, textarea {
+ background: rgba(0, 0, 0, 0.1)!important;
+ border: 1px solid rgba(0, 0, 0, 0.2)!important;
+ border-radius: 2px !important;
+}
+div.post.reply.highlighted {
+ background: #f0c0b0;
+ border-color: #d9bfb7;
+}
+div.post.reply p.body a {
+ color: navy;
+}
+p.intro span.subject {
+ color: #000;
+}
+form table tr th {
+ background: #EA8;
+}
+div.ban h2 {
+ background: #FCA;
+ color: inherit;
+}
+div.ban {
+ border-color: #000;
+}
+div.ban p {
+ color: black;
+}
+div.pages {
+ background: #F0E0D6;
+ border-color: #D9BFB7;
+}
+div.pages a.selected {
+ color: #800;
+}
+hr {
+ border-color: rgba(0, 0, 0, 0.2);
+}
+div.boardlist {
+ color: rgba(0, 0, 0, 0.2);
+}
+div.boardlist a {
+ color: #000;
+}
+table.modlog tr th {
+ background: #EA8;
+}
diff --git a/templates/banned.html b/templates/banned.html
index e912c1a9..e7b626f6 100644
--- a/templates/banned.html
+++ b/templates/banned.html
@@ -1,9 +1,17 @@
{% filter remove_whitespace %}
{# Automatically removes unnecessary whitespace #}
<div class="ban">
- <h2>{% trans %}You are banned! ;_;{% endtrans %}</h2>
+ {% if ban.expires and time() >= ban.expires %}
+ <h2>{% trans %}You were banned! ;_;{% endtrans %}</h2>
+ {% else %}
+ <h2>{% trans %}You are banned! ;_;{% endtrans %}</h2>
+ {% endif %}
<p>
- {% trans %}You have been banned from{% endtrans %}
+ {% if ban.expires and time() >= ban.expires %}
+ {% trans %}You were banned from{% endtrans %}
+ {% else %}
+ {% trans %}You have been banned from{% endtrans %}
+ {% endif %}
{% if ban.board %}
<strong>{{ config.board_abbreviation|sprintf(ban.board) }}</strong>
{% else %}
@@ -23,7 +31,9 @@
<p>
{% trans %}Your ban was filed on{% endtrans %}
<strong>{{ ban.set|date(config.ban_date) }}</strong> {% trans %}and{% endtrans %} <span id="expires">
- {% if ban.expires %}
+ {% if ban.expires and time() >= ban.expires %}
+ {% trans %} has since expired. Refresh the page to continue.{% endtrans %}
+ {% elseif ban.expires %}
{% trans %}expires{% endtrans %} <span id="countdown">{{ ban.expires|until }}</span> {% trans %}from now, which is on{% endtrans %}
<strong>
{{ ban.expires|date(config.ban_date) }}
diff --git a/templates/boardlist.html b/templates/boardlist.html
new file mode 100644
index 00000000..62646845
--- /dev/null
+++ b/templates/boardlist.html
@@ -0,0 +1 @@
+I'm your overboard boardlist. You can put here anything and I reside in templates/boardlist.html
diff --git a/templates/index.html b/templates/index.html
index eaed3666..451ca8b1 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,9 +1,21 @@
<!doctype html>
<html>
<head>
- {% include 'header.html' %}
<meta charset="utf-8">
- <title>{{ board.url }} - {{ board.name }}</title>
+
+ <script type="text/javascript">
+ {% if config.quick_reply %}
+ var new_reply_string = "{{ config.button_reply }}";
+ {% endif %}
+ {% if not no_post_form %}
+ var active_page = "index";
+ {% else %}
+ var active_page = "ukko";
+ {% endif %}
+ </script>
+
+ {% include 'header.html' %}
+ <title>{{ board.url }} - {{ board.title|e }}</title>
</head>
<body>
{{ boardlist.top }}
@@ -20,7 +32,11 @@
</header>
{% include 'attention_bar.html' %}
- {% include 'post_form.html' %}
+ {% if not no_post_form %}
+ {% include 'post_form.html' %}
+ {% else %}
+ {% include 'boardlist.html' %}
+ {% endif %}
{% if config.blotter %}<hr /><div class="blotter">{{ config.blotter }}</div>{% endif %}
<hr />
diff --git a/templates/main.js b/templates/main.js
index b0a96393..31c0254a 100644
--- a/templates/main.js
+++ b/templates/main.js
@@ -1,5 +1,27 @@
{% raw %}
+/* gettext-compatible _ function, example of usage:
+ *
+ * > // Loading pl_PL.json here (containing polish translation strings generated by tools/i18n_compile.php)
+ * > alert(_("Hello!"));
+ * Witaj!
+ */
+function _(s) {
+ return (typeof l10n != 'undefined' && typeof l10n[s] != 'undefined') ? l10n[s] : s;
+}
+
+/* printf-like formatting function, example of usage:
+ *
+ * > alert(fmt("There are {0} birds on {1} trees", [3,4]));
+ * There are 3 birds on 4 trees
+ * > // Loading pl_PL.json here (containing polish translation strings generated by tools/locale_compile.php)
+ * > alert(fmt(_("{0} users"), [3]));
+ * 3 uzytkownikow
+ */
+function fmt(s,a) {
+ return s.replace(/\{([0-9]+)\}/g, function(x) { return a[x[1]]; });
+}
+
var saved = {};
@@ -105,7 +127,7 @@ function generatePassword() {
function dopost(form) {
if (form.elements['name']) {
- localStorage.name = form.elements['name'].value.replace(/ ##.+$/, '');
+ localStorage.name = form.elements['name'].value.replace(/( |^)## .+$/, '');
}
if (form.elements['email'] && form.elements['email'].value != 'sage') {
localStorage.email = form.elements['email'].value;
diff --git a/templates/mod/ban_list.html b/templates/mod/ban_list.html
index 2edb9cdf..f8826ade 100644
--- a/templates/mod/ban_list.html
+++ b/templates/mod/ban_list.html
@@ -10,6 +10,7 @@
<th>{% trans 'Set' %}</th>
<th>{% trans 'Duration' %}</th>
<th>{% trans 'Expires' %}</th>
+ <th>{% trans 'Seen' %}</th>
<th>{% trans 'Staff' %}</th>
</tr>
{% for ban in bans %}
@@ -59,6 +60,13 @@
{% endif %}
</td>
<td>
+ {% if ban.seen %}
+ {% trans 'Yes' %}
+ {% else %}
+ {% trans 'No' %}
+ {% endif %}
+ </td>
+ <td>
{% if ban.username %}
{% if mod|hasPermission(config.mod.view_banstaff) %}
<a href="?/new_PM/{{ ban.username|e }}">{{ ban.username|e }}</a>
diff --git a/templates/mod/dashboard.html b/templates/mod/dashboard.html
index 2f0b03ad..e54b3cd3 100644
--- a/templates/mod/dashboard.html
+++ b/templates/mod/dashboard.html
@@ -101,6 +101,7 @@
</ul>
</fieldset>
+{#
<fieldset>
<legend>{% trans 'Search' %}</legend>
@@ -115,6 +116,7 @@
</li>
</ul>
</fieldset>
+#}
{% if config.debug %}
<fieldset>
diff --git a/templates/mod/view_ip.html b/templates/mod/view_ip.html
index c49880ae..e2dad7af 100644
--- a/templates/mod/view_ip.html
+++ b/templates/mod/view_ip.html
@@ -137,6 +137,16 @@
</td>
</tr>
<tr>
+ <th>{% trans 'Seen' %}</th>
+ <td>
+ {% if ban.seen %}
+ {% trans 'Yes' %}
+ {% else %}
+ {% trans 'No' %}
+ {% endif %}
+ </td>
+ </tr>
+ <tr>
<th>{% trans 'Staff' %}</th>
<td>
{% if ban.username %}
@@ -161,3 +171,43 @@
{% include 'mod/ban_form.html' %}
</fieldset>
{% endif %}
+
+{% if logs|count > 0 %}
+ <fieldset id="history">
+ <legend>History</legend>
+ <table class="modlog" style="width:100%">
+ <tr>
+ <th>{% trans 'Staff' %}</th>
+ <th>{% trans 'Time' %}</th>
+ <th>{% trans 'Board' %}</th>
+ <th>{% trans 'Action' %}</th>
+ </tr>
+ {% for log in logs %}
+ <tr>
+ <td class="minimal">
+ {% if log.username %}
+ <a href="?/log:{{ log.username|e }}">{{ log.username|e }}</a>
+ {% elseif log.mod == -1 %}
+ <em>system</em>
+ {% else %}
+ <em>{% trans 'deleted?' %}</em>
+ {% endif %}
+ </td>
+ <td class="minimal">
+ <span title="{{ log.time|date(config.post_date) }}">{{ log.time|ago }}</span>
+ </td>
+ <td class="minimal">
+ {% if log.board %}
+ <a href="?/{{ config.board_path|sprintf(log.board) }}{{ config.file_index }}">{{ config.board_abbreviation|sprintf(log.board) }}</a>
+ {% else %}
+ -
+ {% endif %}
+ </td>
+ <td>
+ {{ log.text }}
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ </fieldset>
+{% endif %}
diff --git a/templates/page.html b/templates/page.html
index 6e7a8a18..07eea62e 100644
--- a/templates/page.html
+++ b/templates/page.html
@@ -1,8 +1,8 @@
<!doctype html>
<html>
<head>
- {% include 'header.html' %}
<meta charset="utf-8">
+ {% include 'header.html' %}
<title>{{ title }}</title>
</head>
<body>
diff --git a/templates/post_form.html b/templates/post_form.html
index 01b642d3..64451fb9 100755
--- a/templates/post_form.html
+++ b/templates/post_form.html
@@ -24,24 +24,22 @@
<td>
<input type="text" name="email" size="25" maxlength="40" autocomplete="off">
{{ antibot.html() }}
+ {% if not (not (config.field_disable_subject or (id and config.field_disable_reply_subject)) or (mod and post.mod|hasPermission(config.mod.bypass_field_disable, board.uri))) %}
+ <input accesskey="s" style="margin-left:2px;" type="submit" name="post" value="{% if id %}{{ config.button_reply }}{% else %}{{ config.button_newtopic }}{% endif %}" />{% if config.spoiler_images %} <input id="spoiler" name="spoiler" type="checkbox"> <label for="spoiler">{% trans %}Spoiler Image{% endtrans %}</label>{% endif %}
+ {% endif %}
</td>
</tr>{% endif %}
- <tr>
- {% if not (config.field_disable_subject or (id and config.field_disable_reply_subject)) or (mod and post.mod|hasPermission(config.mod.bypass_field_disable, board.uri)) %}<th>
+ {% if not (config.field_disable_subject or (id and config.field_disable_reply_subject)) or (mod and post.mod|hasPermission(config.mod.bypass_field_disable, board.uri)) %}<tr>
+ <th>
{% trans %}Subject{% endtrans %}
{{ antibot.html() }}
</th>
<td>
<input style="float:left;" type="text" name="subject" size="25" maxlength="100" autocomplete="off">
- {% else %}<th>
- {% trans %}Submit{% endtrans %}
- {{ antibot.html() }}
- </th>
- <td>
- {% endif %}
<input accesskey="s" style="margin-left:2px;" type="submit" name="post" value="{% if id %}{{ config.button_reply }}{% else %}{{ config.button_newtopic }}{% endif %}" />{% if config.spoiler_images %} <input id="spoiler" name="spoiler" type="checkbox"> <label for="spoiler">{% trans %}Spoiler Image{% endtrans %}</label>{% endif %}
</td>
</tr>
+ {% endif %}
<tr>
<th>
{% trans %}Comment{% endtrans %}
@@ -50,6 +48,11 @@
<td>
<textarea name="body" id="body" rows="5" cols="35"></textarea>
{{ antibot.html() }}
+ {% if not (not (config.field_disable_subject or (id and config.field_disable_reply_subject)) or (mod and post.mod|hasPermission(config.mod.bypass_field_disable, board.uri))) %}
+ {% if not (not config.field_disable_email or (mod and post.mod|hasPermission(config.mod.bypass_field_disable, board.uri))) %}
+ <input accesskey="s" style="margin-left:2px;" type="submit" name="post" value="{% if id %}{{ config.button_reply }}{% else %}{{ config.button_newtopic }}{% endif %}" />{% if config.spoiler_images %} <input id="spoiler" name="spoiler" type="checkbox"> <label for="spoiler">{% trans %}Spoiler Image{% endtrans %}</label>{% endif %}
+ {% endif %}
+ {% endif %}
</td>
</tr>
{% if config.recaptcha %}
@@ -59,7 +62,7 @@
{{ antibot.html() }}
</th>
<td>
- <script type="text/javascript" src="http://www.google.com/recaptcha/api/challenge?k={{ config.recaptcha_public }}"></script>
+ <script type="text/javascript" src="//www.google.com/recaptcha/api/challenge?k={{ config.recaptcha_public }}"></script>
{{ antibot.html() }}
</td>
</tr>
diff --git a/templates/post_reply.html b/templates/post_reply.html
index 73ff2c15..450f8246 100644
--- a/templates/post_reply.html
+++ b/templates/post_reply.html
@@ -7,14 +7,14 @@
<label for="delete_{{ post.id }}">
{% if post.subject|length > 0 %}
{# show subject #}
- <span class="subject">{{ post.subject }}</span>
+ <span class="subject">{{ post.subject|bidi_cleanup }}</span>
{% endif %}
{% if post.email|length > 0 %}
{# start email #}
<a class="email" href="mailto:{{ post.email }}">
{% endif %}
{% set capcode = post.capcode|capcode %}
- <span {% if capcode.name %}style="{{ capcode.name }}" {% endif %}class="name">{{ post.name }}</span>
+ <span {% if capcode.name %}style="{{ capcode.name }}" {% endif %}class="name">{{ post.name|bidi_cleanup }}</span>
{% if post.trip|length > 0 %}
<span {% if capcode.trip %}style="{{ capcode.trip }}" {% endif %}class="trip">{{ post.trip }}</span>
{% endif %}
@@ -66,9 +66,9 @@
{% if config.show_filename and post.filename %}
,
{% if post.filename|length > config.max_filename_display %}
- <span title="{{ post.filename }}">{{ post.filename|truncate(config.max_filename_display) }}</span>
+ <span class="postfilename" title="{{ post.filename|bidi_cleanup }}">{{ post.filename|truncate(config.max_filename_display)|bidi_cleanup }}</span>
{% else %}
- {{ post.filename }}
+ <span class="postfilename">{{ post.filename|bidi_cleanup }}</span>
{% endif %}
{% endif %}
{% if post.thumb != 'file' and config.image_identification %}
diff --git a/templates/post_thread.html b/templates/post_thread.html
index 6bc0694f..edc07011 100644
--- a/templates/post_thread.html
+++ b/templates/post_thread.html
@@ -1,152 +1,148 @@
-{% filter remove_whitespace %}
-{# tabs and new lines will be ignored #}
-
-<div id="thread_{{ post.id }}">
-
-{% if post.embed %}
- {{ post.embed }}
-{% elseif post.file == 'deleted' %}
- <img src="{{ config.image_deleted }}" alt="" />
-{% elseif post.file and post.file %}
- <p class="fileinfo">{% trans %}File:{% endtrans %} <a href="{{ config.uri_img }}{{ post.file }}">{{ post.file }}</a> <span class="unimportant">
- (
- {% if post.thumb == 'spoiler' %}
- {% trans %}Spoiler Image{% endtrans %},
- {% endif %}
- {{ post.filesize|filesize }}
- {% if post.filex and post.filey %}
- , {{ post.filex}}x{{ post.filey }}
- {% if config.show_ratio %}
- , {{ post.ratio }}
- {% endif %}
- {% endif %}
- {% if config.show_filename and post.filename %}
- ,
- {% if post.filename|length > config.max_filename_display %}
- <span title="{{ post.filename }}">{{ post.filename|truncate(config.max_filename_display) }}</span>
- {% else %}
- {{ post.filename }}
- {% endif %}
- {% endif %}
- {% if post.thumb != 'file' and config.image_identification %}
- ,
- <span class='image_id'>
- <a href="http://imgops.com/{{ config.domain }}{{ config.uri_img }}{{ post.file }}">io</a>
- {% if post.file|extension == 'jpg' %}
- <a href="http://regex.info/exif.cgi?url={{ config.domain }}{{ config.uri_img }}{{ post.file }}">e</a>
- {% endif %}
- <a href="http://www.google.com/searchbyimage?image_url={{ config.domain }}{{ config.uri_img }}{{ post.file }}">g</a>
- <a href="http://www.tineye.com/search?url={{ config.domain }}{{ config.uri_img }}{{ post.file }}">t</a>
- </span>
- {% endif %}
- )
- </span></p>
-<a href="{{ config.uri_img }}{{ post.file }}" target="_blank"{% if post.thumb == 'file' %} class="file"{% endif %}>
-<img src="
- {% if post.thumb == 'file' %}
- {{ config.root }}
- {% if config.file_icons[post.filename|extension] %}
- {{ config.file_thumb|sprintf(config.file_icons[post.filename|extension]) }}
- {% else %}
- {{ config.file_thumb|sprintf(config.file_icons.default) }}
- {% endif %}
- {% elseif post.thumb == 'spoiler' %}
- {{ config.root }}{{ config.spoiler_image }}
- {% else %}
- {{ config.uri_thumb }}{{ post.thumb }}
- {% endif %}" style="width:{{ post.thumbx }}px;height:{{ post.thumby }}px" alt="" /></a>
-{% endif %}
-<div class="post op"><p class="intro"{% if not index %} id="{{ post.id }}"{% endif %}>
- <input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />
- <label for="delete_{{ post.id }}">
- {% if post.subject|length > 0 %}
- {# show subject #}
- <span class="subject">{{ post.subject }}</span>
- {% endif %}
- {% if post.email|length > 0 %}
- {# start email #}
- <a class="email" href="mailto:{{ post.email }}">
- {% endif %}
- {% set capcode = post.capcode|capcode %}
- <span {% if capcode.name %}style="{{ capcode.name }}" {% endif %}class="name">{{ post.name }}</span>
- {% if post.trip|length > 0 %}
- <span {% if capcode.trip %}style="{{ capcode.trip }}" {% endif %}class="trip">{{ post.trip }}</span>
- {% endif %}
- {% if post.email|length > 0 %}
- {# end email #}
- </a>
- {% endif %}
- {% if capcode %}
- {{ capcode.cap }}
- {% endif %}
- {% if post.mod and post.mod|hasPermission(config.mod.show_ip, board.uri) %}
- [<a style="margin:0;" href="?/IP/{{ post.ip }}">{{ post.ip }}</a>]
- {% endif %}
-
- <time datetime="{{ post.time|date('%Y-%m-%dT%H:%M:%S') }}{{ timezone() }}">{{ post.time|date(config.post_date) }}</time>
- </label>
- {% if config.poster_ids %}
- ID: {{ post.ip|poster_id(post.id) }}
- {% endif %}
- <a class="post_no" href="http://webchat.6irc.net/?channels=vichan-int-{{ board.uri }}-{{ post.id }}&nick=Anon....">
- #</a>
-
- <a class="post_no p1" href="{{ post.link }}">No.</a>
- <a class="post_no p2"
- {% if not index %}
- onclick="citeReply({{ post.id }});"
- {% endif %}
- href="{% if index %}
- {{ post.link('q') }}
- {% else %}
- javascript:void(0);
- {% endif %}">
- {{ post.id }}
- </a>
- {% if post.sticky %}
- <img class="icon" title="Sticky" src="{{ config.image_sticky }}" alt="Sticky" />
- {% endif %}
- {% if post.locked %}
- <img class="icon" title="Locked" src="{{ config.image_locked }}" alt="Locked" />
- {% endif %}
- {% if post.bumplocked and (config.mod.view_bumplock < 0 or (post.mod and post.mod|hasPermission(config.mod.view_bumplock, board.uri))) %}
- <img class="icon" title="Bumplocked" src="{{ config.image_bumplocked }}" alt="Bumplocked" />
- {% endif %}
- {% if index %}
- <a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ config.file_page|sprintf(post.id) }}">[{% trans %}Reply{% endtrans %}]</a>
- {% endif %}
- {{ post.postControls }}
- </p>
- <div class="body">
- {% endfilter %}{% if index %}{{ post.body|truncate_body(post.link) }}{% else %}{{ post.body }}{% endif %}{% filter remove_whitespace %}
- </div>
- {% if post.omitted or post.omitted_images %}
- <span class="omitted">
- {% if post.omitted %}
- {% trans %}
- 1 post
- {% plural post.omitted %}
- {{ count }} posts
- {% endtrans %}
- {% if post.omitted_images %}
- {% trans %}and{% endtrans %}
- {% endif %}
- {% endif %}
- {% if post.omitted_images %}
- {% trans %}
- 1 image reply
- {% plural post.omitted_images %}
- {{ count }} image replies
- {% endtrans %}
- {% endif %} {% trans %}omitted. Click reply to view.{% endtrans %}
- </span>
- {% endif %}
-{% if not index %}
-{% endif %}
-</div>{% endfilter %}
-{% set hr = post.hr %}
-{% for post in post.posts %}
- {% include 'post_reply.html' %}
-{% endfor %}
-<br class="clear"/>{% if hr %}<hr/>{% endif %}
-</div>
+{% filter remove_whitespace %}
+{# tabs and new lines will be ignored #}
+
+<div id="thread_{{ post.id }}" data-board="{{ board.uri }}">
+
+{% if post.embed %}
+ {{ post.embed }}
+{% elseif post.file == 'deleted' %}
+ <img src="{{ config.image_deleted }}" alt="" />
+{% elseif post.file and post.file %}
+ <p class="fileinfo">{% trans %}File:{% endtrans %} <a href="{{ config.uri_img }}{{ post.file }}">{{ post.file }}</a> <span class="unimportant">
+ (
+ {% if post.thumb == 'spoiler' %}
+ {% trans %}Spoiler Image{% endtrans %},
+ {% endif %}
+ {{ post.filesize|filesize }}
+ {% if post.filex and post.filey %}
+ , {{ post.filex}}x{{ post.filey }}
+ {% if config.show_ratio %}
+ , {{ post.ratio }}
+ {% endif %}
+ {% endif %}
+ {% if config.show_filename and post.filename %}
+ ,
+ {% if post.filename|length > config.max_filename_display %}
+ <span class="postfilename" title="{{ post.filename|bidi_cleanup }}">{{ post.filename|truncate(config.max_filename_display)|bidi_cleanup }}</span>
+ {% else %}
+ <span class="postfilename">{{ post.filename|bidi_cleanup }}</span>
+ {% endif %}
+ {% endif %}
+ {% if post.thumb != 'file' and config.image_identification %}
+ ,
+ <span class='image_id'>
+ <a href="http://imgops.com/{{ config.domain }}{{ config.uri_img }}{{ post.file }}">io</a>
+ {% if post.file|extension == 'jpg' %}
+ <a href="http://regex.info/exif.cgi?url={{ config.domain }}{{ config.uri_img }}{{ post.file }}">e</a>
+ {% endif %}
+ <a href="http://www.google.com/searchbyimage?image_url={{ config.domain }}{{ config.uri_img }}{{ post.file }}">g</a>
+ <a href="http://www.tineye.com/search?url={{ config.domain }}{{ config.uri_img }}{{ post.file }}">t</a>
+ </span>
+ {% endif %}
+ )
+ </span></p>
+<a href="{{ config.uri_img }}{{ post.file }}" target="_blank"{% if post.thumb == 'file' %} class="file"{% endif %}>
+<img src="
+ {% if post.thumb == 'file' %}
+ {{ config.root }}
+ {% if config.file_icons[post.filename|extension] %}
+ {{ config.file_thumb|sprintf(config.file_icons[post.filename|extension]) }}
+ {% else %}
+ {{ config.file_thumb|sprintf(config.file_icons.default) }}
+ {% endif %}
+ {% elseif post.thumb == 'spoiler' %}
+ {{ config.root }}{{ config.spoiler_image }}
+ {% else %}
+ {{ config.uri_thumb }}{{ post.thumb }}
+ {% endif %}" style="width:{{ post.thumbx }}px;height:{{ post.thumby }}px" alt="" /></a>
+{% endif %}
+<div class="post op"><p class="intro"{% if not index %} id="{{ post.id }}"{% endif %}>
+ <input type="checkbox" class="delete" name="delete_{{ post.id }}" id="delete_{{ post.id }}" />
+ <label for="delete_{{ post.id }}">
+ {% if post.subject|length > 0 %}
+ {# show subject #}
+ <span class="subject">{{ post.subject|bidi_cleanup }}</span>
+ {% endif %}
+ {% if post.email|length > 0 %}
+ {# start email #}
+ <a class="email" href="mailto:{{ post.email }}">
+ {% endif %}
+ {% set capcode = post.capcode|capcode %}
+ <span {% if capcode.name %}style="{{ capcode.name }}" {% endif %}class="name">{{ post.name|bidi_cleanup }}</span>
+ {% if post.trip|length > 0 %}
+ <span {% if capcode.trip %}style="{{ capcode.trip }}" {% endif %}class="trip">{{ post.trip }}</span>
+ {% endif %}
+ {% if post.email|length > 0 %}
+ {# end email #}
+ </a>
+ {% endif %}
+ {% if capcode %}
+ {{ capcode.cap }}
+ {% endif %}
+ {% if post.mod and post.mod|hasPermission(config.mod.show_ip, board.uri) %}
+ [<a style="margin:0;" href="?/IP/{{ post.ip }}">{{ post.ip }}</a>]
+ {% endif %}
+ <time datetime="{{ post.time|date('%Y-%m-%dT%H:%M:%S') }}{{ timezone() }}">{{ post.time|date(config.post_date) }}</time>
+ </label>
+ {% if config.poster_ids %}
+ ID: {{ post.ip|poster_id(post.id) }}
+ {% endif %}
+ <a class="post_no" href="{{ post.link }}">No.</a>
+ <a class="post_no"
+ {% if not index %}
+ onclick="citeReply({{ post.id }});"
+ {% endif %}
+ href="{% if index %}
+ {{ post.link('q') }}
+ {% else %}
+ javascript:void(0);
+ {% endif %}">
+ {{ post.id }}
+ </a>
+ {% if post.sticky %}
+ <img class="icon" title="Sticky" src="{{ config.image_sticky }}" alt="Sticky" />
+ {% endif %}
+ {% if post.locked %}
+ <img class="icon" title="Locked" src="{{ config.image_locked }}" alt="Locked" />
+ {% endif %}
+ {% if post.bumplocked and (config.mod.view_bumplock < 0 or (post.mod and post.mod|hasPermission(config.mod.view_bumplock, board.uri))) %}
+ <img class="icon" title="Bumplocked" src="{{ config.image_bumplocked }}" alt="Bumplocked" />
+ {% endif %}
+ {% if index %}
+ <a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ config.file_page|sprintf(post.id) }}">[{% trans %}Reply{% endtrans %}]</a>
+ {% endif %}
+ {{ post.postControls }}
+ </p>
+ <div class="body">
+ {% endfilter %}{% if index %}{{ post.body|truncate_body(post.link) }}{% else %}{{ post.body }}{% endif %}{% filter remove_whitespace %}
+ </div>
+ {% if post.omitted or post.omitted_images %}
+ <span class="omitted">
+ {% if post.omitted %}
+ {% trans %}
+ 1 post
+ {% plural post.omitted %}
+ {{ count }} posts
+ {% endtrans %}
+ {% if post.omitted_images %}
+ {% trans %}and{% endtrans %}
+ {% endif %}
+ {% endif %}
+ {% if post.omitted_images %}
+ {% trans %}
+ 1 image reply
+ {% plural post.omitted_images %}
+ {{ count }} image replies
+ {% endtrans %}
+ {% endif %} {% trans %}omitted. Click reply to view.{% endtrans %}
+ </span>
+ {% endif %}
+{% if not index %}
+{% endif %}
+</div>{% endfilter %}
+{% set hr = post.hr %}
+{% for post in post.posts %}
+ {% include 'post_reply.html' %}
+{% endfor %}
+<br class="clear"/>{% if hr %}<hr/>{% endif %}
+</div> \ No newline at end of file
diff --git a/templates/themes/basic/theme.php b/templates/themes/basic/theme.php
index 806a331a..7ba0d1e7 100644
--- a/templates/themes/basic/theme.php
+++ b/templates/themes/basic/theme.php
@@ -1,7 +1,7 @@
<?php
require 'info.php';
- function basic_build($action, $settings) {
+ function basic_build($action, $settings, $board) {
// Possible values for $action:
// - all (rebuild everything, initialization)
// - news (news has been updated)
diff --git a/templates/themes/catalog/catalog.css b/templates/themes/catalog/catalog.css
new file mode 100644
index 00000000..048dd656
--- /dev/null
+++ b/templates/themes/catalog/catalog.css
@@ -0,0 +1,38 @@
+img {
+ float:none!important;
+ margin: auto;
+ margin-bottom: 12px;
+ max-height: 150px;
+ max-width: 200px;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.55);
+ border: 2px solid rgba(153, 153, 153, 0);
+}
+
+/*
+img:hover {
+ border: 2px solid rgba(153, 153, 153, 0.27);
+}
+*/
+
+div.thread {
+ display: inline-block;
+ vertical-align: top;
+ margin-bottom:25px;
+ margin-left: 20px;
+ margin-right: 15px;
+ text-align:center;
+ font-weight:normal;
+ width:205px;
+ overflow:hidden;
+ position: relative;
+ font-size:11px;
+ padding: 15px;
+ background: rgba(182, 182, 182, 0.12);
+ border: 2px solid rgba(111, 111, 111, 0.34);
+ max-height:300px;
+}
+
+div.thread:hover {
+ background: #D6DAF0;
+ border-color: #B7C5D9;
+} \ No newline at end of file
diff --git a/templates/themes/catalog/catalog.html b/templates/themes/catalog/catalog.html
new file mode 100644
index 00000000..7b8c075a
--- /dev/null
+++ b/templates/themes/catalog/catalog.html
@@ -0,0 +1,36 @@
+{% filter remove_whitespace %}
+<!doctype html>
+<html>
+<head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <title>{{ settings.title }}</title>
+ <link rel="stylesheet" media="screen" href="{{ config.url_stylesheet }}"/>
+ <link rel="stylesheet" media="screen" href="{{ config.root }}{{ settings.css }}"/>
+ {% if config.url_favicon %}<link rel="shortcut icon" href="{{ config.url_favicon }}" />{% endif %}
+</head>
+<body>
+ {{ boardlist.top }}
+ <header>
+ <h1>{{ settings.title }} (<a href="{{link}}">/{{ board }}/</a>)</h1>
+ <div class="subtitle">{{ settings.subtitle }}</div>
+ </header>
+
+ <ul>
+ {% for post in recent_posts %}
+ <div class="thread">
+ <a href="{{post.link}}">
+ <img src="{{post.file}}" class="{{post.board}}" title="{{post.bump|date('%b %d %H:%M')}}">
+ </a>
+ <span class="replies">
+ <strong>{% trans %}1 reply{% plural post.reply_count %}{{ count }} replies{% endtrans %}</strong><br/>
+ {{ post.body }}
+ </span>
+ </div>
+ {% endfor %}
+ </ul>
+
+ <hr/>
+ <p class="unimportant" style="margin-top:20px;text-align:center;">Powered by <a href="http://tinyboard.org/">Tinyboard</a> {{ config.version }} | <a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2013 Tinyboard Development Group</p>
+</body>
+</html>
+{% endfilter %}
diff --git a/templates/themes/catalog/info.php b/templates/themes/catalog/info.php
new file mode 100644
index 00000000..1b9831ae
--- /dev/null
+++ b/templates/themes/catalog/info.php
@@ -0,0 +1,42 @@
+<?php
+ $theme = array();
+
+ // Theme name
+ $theme['name'] = 'Catalog';
+ // Description (you can use Tinyboard markup here)
+ $theme['description'] = 'Show a post catalog.';
+ $theme['version'] = 'v0.1';
+
+ // Theme configuration
+ $theme['config'] = Array();
+
+ $theme['config'][] = Array(
+ 'title' => 'Title',
+ 'name' => 'title',
+ 'type' => 'text',
+ 'default' => 'Catalog'
+ );
+
+ $__boards = listBoards();
+ $__default_boards = Array();
+ foreach ($__boards as $__board)
+ $__default_boards[] = $__board['uri'];
+
+ $theme['config'][] = Array(
+ 'title' => 'Included boards',
+ 'name' => 'boards',
+ 'type' => 'text',
+ 'comment' => '(space seperated)',
+ 'default' => implode(' ', $__default_boards)
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'CSS file',
+ 'name' => 'css',
+ 'type' => 'text',
+ 'default' => 'catalog.css',
+ 'comment' => '(eg. "catalog.css")'
+ );
+
+ // Unique function name for building everything
+ $theme['build_function'] = 'catalog_build';
diff --git a/templates/themes/catalog/theme.php b/templates/themes/catalog/theme.php
new file mode 100644
index 00000000..e585cf17
--- /dev/null
+++ b/templates/themes/catalog/theme.php
@@ -0,0 +1,60 @@
+<?php
+ require 'info.php';
+
+ function catalog_build($action, $settings, $board) {
+ global $config;
+
+ // Possible values for $action:
+ // - all (rebuild everything, initialization)
+ // - news (news has been updated)
+ // - boards (board list changed)
+ // - post (a reply has been made)
+ // - post-thread (a thread has been made)
+
+ $boards = explode(' ', $settings['boards']);
+
+ if ($action == 'all') {
+ copy('templates/themes/catalog/catalog.css', $config['dir']['home'] . $settings['css']);
+
+ foreach ($boards as $board) {
+ $b = new Catalog();
+ $b->build($settings, $board);
+ }
+ } elseif ($action == 'post-thread' && in_array($board, $boards)) {
+ $b = new Catalog();
+ $b->build($settings, $board);
+ }
+ }
+
+ // Wrap functions in a class so they don't interfere with normal Tinyboard operations
+ class Catalog {
+ public function build($settings, $board_name) {
+ global $config, $board;
+
+ openBoard($board_name);
+
+ $recent_images = array();
+ $recent_posts = array();
+ $stats = array();
+
+ $query = query(sprintf("SELECT *, `id` AS `thread_id`, (SELECT COUNT(*) FROM `posts_%s` WHERE `thread` = `thread_id`) AS `reply_count`, '%s' AS `board` FROM `posts_%s` WHERE `thread` IS NULL ORDER BY `bump` DESC", $board_name, $board_name, $board_name)) or error(db_error());
+
+ while ($post = $query->fetch()) {
+ $post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], ($post['thread'] ? $post['thread'] : $post['id']));
+ $post['board_name'] = $board['name'];
+ $post['file'] = $config['uri_thumb'] . $post['thumb'];
+ $recent_posts[] = $post;
+ }
+
+ file_write($config['dir']['home'] . $board_name . '/catalog.html', Element('themes/catalog/catalog.html', Array(
+ 'settings' => $settings,
+ 'config' => $config,
+ 'boardlist' => createBoardlist(),
+ 'recent_images' => $recent_images,
+ 'recent_posts' => $recent_posts,
+ 'stats' => $stats,
+ 'board' => $board_name,
+ 'link' => $config['root'] . $board['dir']
+ )));
+ }
+ };
diff --git a/templates/themes/categories/theme.php b/templates/themes/categories/theme.php
index 11fcbbaf..7d468d0d 100644
--- a/templates/themes/categories/theme.php
+++ b/templates/themes/categories/theme.php
@@ -1,7 +1,7 @@
<?php
require 'info.php';
- function categories_build($action, $settings) {
+ function categories_build($action, $settings, $board) {
// Possible values for $action:
// - all (rebuild everything, initialization)
// - news (news has been updated)
diff --git a/templates/themes/frameset/theme.php b/templates/themes/frameset/theme.php
index 015eebaf..5fd9d29e 100644
--- a/templates/themes/frameset/theme.php
+++ b/templates/themes/frameset/theme.php
@@ -1,7 +1,7 @@
<?php
require 'info.php';
- function frameset_build($action, $settings) {
+ function frameset_build($action, $settings, $board) {
// Possible values for $action:
// - all (rebuild everything, initialization)
// - news (news has been updated)
diff --git a/templates/themes/recent/theme.php b/templates/themes/recent/theme.php
index b436ab0f..75907e77 100644
--- a/templates/themes/recent/theme.php
+++ b/templates/themes/recent/theme.php
@@ -1,12 +1,13 @@
<?php
require 'info.php';
- function recentposts_build($action, $settings) {
+ function recentposts_build($action, $settings, $board) {
// Possible values for $action:
// - all (rebuild everything, initialization)
// - news (news has been updated)
// - boards (board list changed)
// - post (a post has been made)
+ // - post-thread (a thread has been made)
$b = new RecentPosts();
$b->build($action, $settings);
@@ -23,7 +24,7 @@
$this->excluded = explode(' ', $settings['exclude']);
- if ($action == 'all' || $action == 'post')
+ if ($action == 'all' || $action == 'post' || $action == 'post-thread')
file_write($config['dir']['home'] . $settings['html'], $this->homepage($settings));
}
diff --git a/templates/themes/rrdtool/theme.php b/templates/themes/rrdtool/theme.php
index eed2ac9a..68fc1850 100644
--- a/templates/themes/rrdtool/theme.php
+++ b/templates/themes/rrdtool/theme.php
@@ -1,7 +1,7 @@
<?php
require 'info.php';
- function rrdtool_build($action, $settings) {
+ function rrdtool_build($action, $settings, $board) {
// Possible values for $action:
// - all (rebuild everything, initialization)
// - news (news has been updated)
diff --git a/templates/themes/sitemap/info.php b/templates/themes/sitemap/info.php
new file mode 100644
index 00000000..1198c2e3
--- /dev/null
+++ b/templates/themes/sitemap/info.php
@@ -0,0 +1,53 @@
+<?php
+ $theme = Array();
+
+ // Theme name
+ $theme['name'] = 'Sitemap Generator';
+ // Description (you can use Tinyboard markup here)
+ $theme['description'] = 'Generates an XML sitemap to help search engines find all threads and pages.';
+ $theme['version'] = 'v1.0';
+
+ // Theme configuration
+ $theme['config'] = Array();
+
+ $theme['config'][] = Array(
+ 'title' => 'Sitemap Path',
+ 'name' => 'path',
+ 'type' => 'text',
+ 'default' => 'sitemap.xml',
+ 'size' => '20'
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'URL prefix',
+ 'name' => 'url',
+ 'type' => 'text',
+ 'comment' => '(with trailing slash)',
+ 'default' => 'http://' . (isset ($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : "localhost") . $config['root'],
+ 'size' => '20'
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'Thread change frequency',
+ 'name' => 'changefreq',
+ 'type' => 'text',
+ 'comment' => '(eg. "hourly", "daily", etc.)',
+ 'default' => 'hourly',
+ 'size' => '20'
+ );
+
+ $__boards = listBoards();
+ $__default_boards = Array();
+ foreach ($__boards as $__board)
+ $__default_boards[] = $__board['uri'];
+
+ $theme['config'][] = Array(
+ 'title' => 'Boards',
+ 'name' => 'boards',
+ 'type' => 'text',
+ 'comment' => '(boards to include; space seperated)',
+ 'size' => 24,
+ 'default' => implode(' ', $__default_boards)
+ );
+
+ $theme['build_function'] = 'sitemap_build';
diff --git a/templates/themes/sitemap/sitemap.xml b/templates/themes/sitemap/sitemap.xml
new file mode 100644
index 00000000..733da607
--- /dev/null
+++ b/templates/themes/sitemap/sitemap.xml
@@ -0,0 +1,19 @@
+{% filter remove_whitespace %}
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+ {% for board in boards %}
+ <url>
+ <loc>{{ settings.url ~ (config.board_path | format(board)) ~ config.file_index }}</loc>
+ </url>
+ {% endfor %}
+ {% for board, thread_list in threads %}
+ {% for thread in thread_list %}
+ <url>
+ <loc>{{ settings.url ~ (config.board_path | format(board)) ~ config.dir.res ~ (config.file_page | format(thread.thread_id)) }}</loc>
+ <lastmod>{{ thread.lastmod | date('%Y-%m-%dT%H:%M:%S') }}{{ timezone() }}</lastmod>
+ <changefreq>{{ settings.changefreq }}</changefreq>
+ </url>
+ {% endfor %}
+ {% endfor %}
+</urlset>
+{% endfilter %} \ No newline at end of file
diff --git a/templates/themes/sitemap/theme.php b/templates/themes/sitemap/theme.php
new file mode 100644
index 00000000..e210316b
--- /dev/null
+++ b/templates/themes/sitemap/theme.php
@@ -0,0 +1,32 @@
+<?php
+ require 'info.php';
+
+ function sitemap_build($action, $settings, $board) {
+ global $config;
+
+ // Possible values for $action:
+ // - all (rebuild everything, initialization)
+ // - news (news has been updated)
+ // - boards (board list changed)
+ // - post (a post has been made)
+ // - thread (a thread has been made)
+
+ if ($action != 'post' && $action != 'post-thread')
+ return;
+
+ $boards = explode(' ', $settings['boards']);
+
+ $threads = array();
+
+ foreach ($boards as $board) {
+ $query = query(sprintf("SELECT `id` AS `thread_id`, (SELECT `time` FROM `posts_%s` WHERE `thread` = `thread_id` OR `id` = `thread_id` ORDER BY `time` DESC LIMIT 1) AS `lastmod` FROM `posts_%s` WHERE `thread` IS NULL", $board, $board)) or error(db_error());
+ $threads[$board] = $query->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ file_write($settings['path'], Element('themes/sitemap/sitemap.xml', Array(
+ 'settings' => $settings,
+ 'config' => $config,
+ 'threads' => $threads,
+ 'boards' => $boards,
+ )));
+ }
diff --git a/templates/themes/ukko/info.php b/templates/themes/ukko/info.php
new file mode 100644
index 00000000..34d59d4e
--- /dev/null
+++ b/templates/themes/ukko/info.php
@@ -0,0 +1,53 @@
+<?php
+ $theme = Array();
+
+ // Theme name
+ $theme['name'] = 'Ukko';
+ // Description (you can use Tinyboard markup here)
+ $theme['description'] = 'Board with threads and messages from all boards';
+ $theme['version'] = 'v0.1';
+
+ // Theme configuration
+ $theme['config'] = Array();
+
+ $theme['config'][] = Array(
+ 'title' => 'Board name',
+ 'name' => 'title',
+ 'type' => 'text',
+ 'default' => 'Ukko'
+ );
+ $theme['config'][] = Array(
+ 'title' => 'Board URI',
+ 'name' => 'uri',
+ 'type' => 'text',
+ 'comment' => '(ukko for example)'
+ );
+ $theme['config'][] = Array(
+ 'title' => 'Subtitle',
+ 'name' => 'subtitle',
+ 'type' => 'text',
+ 'comment' => '(%s = thread limit. for example "%s freshly bumped threads")'
+ );
+ $theme['config'][] = Array(
+ 'title' => 'Excluded boards',
+ 'name' => 'exclude',
+ 'type' => 'text',
+ 'comment' => '(space seperated)'
+ );
+ $theme['config'][] = Array(
+ 'title' => 'Number of threads',
+ 'name' => 'thread_limit',
+ 'type' => 'text',
+ 'default' => '15',
+ );
+ // Unique function name for building everything
+ $theme['build_function'] = 'ukko_build';
+ $theme['install_callback'] = 'ukko_install';
+
+ if(!function_exists('ukko_install')) {
+ function ukko_install($settings) {
+ if (!file_exists($settings['uri']))
+ @mkdir($settings['uri'], 0777) or error("Couldn't create " . $settings['uri'] . ". Check permissions.", true);
+ }
+ }
+
diff --git a/templates/themes/ukko/theme.php b/templates/themes/ukko/theme.php
new file mode 100644
index 00000000..6fa5fa7e
--- /dev/null
+++ b/templates/themes/ukko/theme.php
@@ -0,0 +1,111 @@
+<?php
+ require 'info.php';
+
+ function ukko_build($action, $settings) {
+ $ukko = new ukko();
+ $ukko->settings = $settings;
+ $ukko->build();
+ }
+
+ class ukko {
+ public $settings;
+ public function build($mod = false) {
+ global $config;
+ $boards = listBoards();
+
+ $body = '';
+ $overflow = array();
+ $board = array(
+ 'url' => $this->settings['uri'],
+ 'name' => $this->settings['title'],
+ 'title' => sprintf($this->settings['subtitle'], $this->settings['thread_limit'])
+ );
+
+ $query = '';
+ foreach($boards as &$_board) {
+ if(in_array($_board['uri'], explode(' ', $this->settings['exclude'])))
+ continue;
+ $query .= sprintf("SELECT *, '%s' AS `board` FROM `posts_%s` WHERE `thread` IS NULL UNION ALL ", $_board['uri'], $_board['uri']);
+ }
+ $query = preg_replace('/UNION ALL $/', 'ORDER BY `bump` DESC', $query);
+ $query = query($query) or error(db_error());
+
+ $count = 0;
+ $threads = array();
+ while($post = $query->fetch()) {
+
+ if(!isset($threads[$post['board']])) {
+ $threads[$post['board']] = 1;
+ } else {
+ $threads[$post['board']] += 1;
+ }
+
+ if($count < $this->settings['thread_limit']) {
+ openBoard($post['board']);
+ $thread = new Thread(
+ $post['id'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'],
+ $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'],
+ $post['filename'], $post['ip'], $post['sticky'], $post['locked'], $post['sage'], $post['embed'], $mod ? '?/' : $config['root'], $mod
+ );
+
+ $posts = prepare(sprintf("SELECT * FROM `posts_%s` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $post['board']));
+ $posts->bindValue(':id', $post['id']);
+ $posts->bindValue(':limit', ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT);
+ $posts->execute() or error(db_error($posts));
+
+ $num_images = 0;
+ while ($po = $posts->fetch()) {
+ if ($po['file'])
+ $num_images++;
+
+ $thread->add(new Post(
+ $po['id'], $post['id'], $po['subject'], $po['email'], $po['name'], $po['trip'], $po['capcode'], $po['body'], $po['time'],
+ $po['thumb'], $po['thumbwidth'], $po['thumbheight'], $po['file'], $po['filewidth'], $po['fileheight'], $po['filesize'],
+ $po['filename'], $po['ip'], $po['embed'], $mod ? '?/' : $config['root'], $mod)
+ );
+
+ }
+ if ($posts->rowCount() == ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) {
+ $ct = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM `posts_%s` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM `posts_%s` WHERE `file` IS NOT NULL AND `thread` = :thread", $post['board'], $post['board']));
+ $ct->bindValue(':thread', $post['id'], PDO::PARAM_INT);
+ $ct->execute() or error(db_error($count));
+
+ $c = $ct->fetch();
+ $thread->omitted = $c['num'] - ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']);
+
+ $c = $ct->fetch();
+ $thread->omitted_images = $c['num'] - $num_images;
+ }
+
+
+ $thread->posts = array_reverse($thread->posts);
+ $body .= '<h2><a href="' . $config['root'] . $post['board'] . '">/' . $post['board'] . '/</a></h2>';
+ $body .= $thread->build(true);
+ } else {
+ $page = 'index';
+ if(floor($threads[$post['board']] / $config['threads_per_page']) > 0) {
+ $page = floor($threads[$post['board']] / $config['threads_per_page']) + 1;
+ }
+ $overflow[] = array('id' => $post['id'], 'board' => $post['board'], 'page' => $page . '.html');
+ }
+
+ $count += 1;
+ }
+
+ $body .= '<script> var overflow = ' . json_encode($overflow) . '</script>';
+ $body .= '<script type="text/javascript" src="ukko.js"></script>';
+
+ file_write($this->settings['uri'] . '/index.html', Element('index.html', array(
+ 'config' => $config,
+ 'board' => $board,
+ 'no_post_form' => true,
+ 'body' => $body,
+ 'boardlist' => createBoardlist($mod)
+ )));
+
+ file_write($this->settings['uri'] . '/ukko.js', Element('themes/ukko/ukko.js', array()));
+ }
+
+ };
+
+?>
diff --git a/templates/themes/ukko/thumb.png b/templates/themes/ukko/thumb.png
new file mode 100644
index 00000000..49c4237b
--- /dev/null
+++ b/templates/themes/ukko/thumb.png
Binary files differ
diff --git a/templates/themes/ukko/ukko.js b/templates/themes/ukko/ukko.js
new file mode 100644
index 00000000..baf0f78d
--- /dev/null
+++ b/templates/themes/ukko/ukko.js
@@ -0,0 +1,37 @@
+var cache = new Array(),
+ thread = false,
+ loading = false;
+$(document).ready(function() {
+ $(window).on('scroll', function() {
+ if($(window).scrollTop() + $(window).height() + 100 > $(document).height() && !loading && overflow.length > 0) {
+ var page = '../' + overflow[0].board + '/' + overflow[0].page;
+ if($.inArray(page, cache) != -1) {
+ thread = $('div#thread_' + overflow[0].id);
+ if(thread.length > 0) {
+ thread.prepend('<h2><a href="/' + overflow[0].board + '/">/' + overflow[0].board + '/</a></h2>');
+ $('div[id*="thread_"]').last().after(thread.attr('data-board', overflow[0].board).css('display', 'block'));
+ overflow.shift();
+ }
+ } else {
+ loading = true;
+ $.get(page, function(data) {
+ cache.push(page);
+
+ $(data).find('div[id*="thread_"]').each(function() {
+ $('body').prepend($(this).css('display', 'none').attr('data-board', overflow[0].board));
+ });
+
+ thread = $('div#thread_' + overflow[0].id + '[data-board="' + overflow[0].board + '"]');
+ if(thread.length > 0) {
+ thread.prepend('<h2><a href="/' + overflow[0].board + '/">/' + overflow[0].board + '/</a></h2>');
+ $('div[id*="thread_"]').last().after(thread.attr('data-board', overflow[0].board).css('display', 'block'));
+ overflow.shift();
+ }
+
+ loading = false;
+ });
+ }
+ }
+ });
+
+}); \ No newline at end of file
diff --git a/templates/thread.html b/templates/thread.html
index a78b0843..1b217c2f 100644
--- a/templates/thread.html
+++ b/templates/thread.html
@@ -2,35 +2,13 @@
<html>
<head>
<meta charset="utf-8">
-<script src='https://int.vichan.net/opportunistic_https.js'></script>
- <link rel="stylesheet" media="screen" href="{{ config.url_stylesheet }}">
- {% if config.url_favicon %}<link rel="shortcut icon" href="{{ config.url_favicon }}">{% endif %}
- <title>{{ board.url }} - {{ board.name }}</title>
- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes">
- {% if config.meta_keywords %}<meta name="keywords" content="{{ config.meta_keywords }}">{% endif %}
- {% if config.default_stylesheet.1 != '' %}<link rel="stylesheet" type="text/css" id="stylesheet" href="{{ config.uri_stylesheets }}{{ config.default_stylesheet.1 }}">{% endif %}
- <script type="text/javascript">var configRoot="{{ config.root }}";</script>
- {% if not nojavascript %}
- <script type="text/javascript" src="{{ config.url_javascript }}"></script>
- {% if not config.additional_javascript_compile %}
- {% for javascript in config.additional_javascript %}<script type="text/javascript" src="{{ config.additional_javascript_url }}{{ javascript }}"></script>{% endfor %}
- {% endif %}
- {% endif %}
- {% if config.recaptcha %}<style type="text/css">{% raw %}
- .recaptcha_image_cell {
- background: none !important;
- }
- table.recaptchatable {
- border: none !important;
- }
- #recaptcha_logo, #recaptcha_tagline {
- display: none;
- float: right;
- }
- .recaptchatable a {
- display: block;
- }
- {% endraw %}</style>{% endif %}
+
+ <script type="text/javascript">
+ var active_page = "thread";
+ </script>
+
+ {% include 'header.html' %}
+ <title>{{ board.url }} - {% if config.thread_subject_in_title and thread.subject %}{{ thread.subject }}{% else %}{{ board.title|e }}{% endif %}</title>
</head>
<body>
{{ boardlist.top }}
diff --git a/tools/benchmark.php b/tools/benchmark.php
new file mode 100644
index 00000000..8c97364c
--- /dev/null
+++ b/tools/benchmark.php
@@ -0,0 +1,52 @@
+#!/usr/bin/php
+<?php
+/*
+ * benchmark.php - benchmarks thumbnailing methods
+ *
+ */
+
+require dirname(__FILE__) . '/inc/cli.php';
+require 'inc/image.php';
+
+// move back to this directory
+chdir(dirname(__FILE__));
+
+if(count($argv) != 2)
+ die("Usage: {$argv[0]} [file]\n");
+
+$file = $argv[1];
+$extension = strtolower(substr($file, strrpos($file, '.') + 1));
+$out = tempnam($config['tmp'], 'thumb');
+$count = 300;
+
+function benchmark($method) {
+ global $config, $file, $extension, $out, $count;
+
+ $config['thumb_method'] = $method;
+
+ printf("Method: %s\nThumbnailing %d times... ", $method, $count);
+
+ $start = microtime(true);
+ for($i = 0; $i < $count; $i++) {
+ $image = new Image($file, $extension);
+ $thumb = $image->resize(
+ $config['thumb_ext'] ? $config['thumb_ext'] : $extension,
+ $config['thumb_width'],
+ $config['thumb_height']
+ );
+
+ $thumb->to($out);
+ $thumb->_destroy();
+ $image->destroy();
+ }
+ $end = microtime(true);
+
+ printf("Took %.2f seconds (%.2f/second; %.2f ms)\n", $end - $start, $rate = ($count / ($end - $start)), 1000 / $rate);
+
+ unlink($out);
+}
+
+benchmark('gd');
+benchmark('imagick');
+benchmark('convert');
+
diff --git a/tools/i18n_compile.php b/tools/i18n_compile.php
new file mode 100755
index 00000000..ba193767
--- /dev/null
+++ b/tools/i18n_compile.php
@@ -0,0 +1,44 @@
+#!/usr/bin/php
+<?php
+
+/*
+ * i18n_compile.php - compiles the i18n
+ *
+ * Options:
+ * -l [locale], --locale=[locale]
+ * Compiles [locale] locale.
+ *
+ */
+
+require dirname(__FILE__) . '/inc/cli.php';
+
+// parse command line
+$opts = getopt('l:', Array('locale:'));
+$options = Array();
+
+$options['locale'] = isset($opts['l']) ? $opts['l'] : (isset($opts['locale']) ? $opts['locale'] : false);
+
+if ($options['locale']) $locales = array($options['locale']);
+else die("Error: no locales specified; use -l switch, eg. -l pl_PL\n");
+
+foreach ($locales as $loc) {
+ if (file_exists ($locdir = "inc/locale/".$loc)) {
+ if (!is_dir ($locdir)) {
+ continue;
+ }
+ }
+ else {
+ die("Error: $locdir does not exist\n");
+ }
+
+ // Generate tinyboard.po
+ if (file_exists($locdir."/LC_MESSAGES/tinyboard.po")) $join = "-j";
+ else $join = "";
+ passthru("cd $locdir/LC_MESSAGES;
+ msgfmt tinyboard.po -o tinyboard.mo");
+
+ // Generate javascript.po
+ passthru("cd tools/inc/lib/jsgettext/;
+ php po2json.php -i ../../../../$locdir/LC_MESSAGES/javascript.po \
+ -o ../../../../$locdir/LC_MESSAGES/javascript.js");
+}
diff --git a/tools/i18n_extract.php b/tools/i18n_extract.php
new file mode 100755
index 00000000..33650b50
--- /dev/null
+++ b/tools/i18n_extract.php
@@ -0,0 +1,47 @@
+#!/usr/bin/php
+<?php
+
+/*
+ * i18n_extract.php - extracts the strings and updates all locales
+ *
+ * Options:
+ * -l [locale], --locale=[locale]
+ * Updates only [locale] locale. If it does not exist yet, we create a new directory.
+ *
+ */
+
+require dirname(__FILE__) . '/inc/cli.php';
+
+// parse command line
+$opts = getopt('l:', Array('locale:'));
+$options = Array();
+
+$options['locale'] = isset($opts['l']) ? $opts['l'] : (isset($opts['locale']) ? $opts['locale'] : false);
+
+$locales = glob("inc/locale/*");
+$locales = array_map("basename", $locales);
+
+if ($options['locale']) $locales = array($options['locale']);
+
+
+foreach ($locales as $loc) {
+ if (file_exists ($locdir = "inc/locale/".$loc)) {
+ if (!is_dir ($locdir)) {
+ continue;
+ }
+ }
+ else {
+ mkdir($locdir);
+ mkdir($locdir."/LC_MESSAGES");
+ }
+
+ // Generate tinyboard.po
+ if (file_exists($locdir."/LC_MESSAGES/tinyboard.po")) $join = "-j";
+ else $join = "";
+ passthru("cd $locdir/LC_MESSAGES;
+ xgettext -d tinyboard -L php --from-code utf-8 $join -c $(find ../../../../ -name \*.php)");
+
+ // Generate javascript.po
+ passthru("cd $locdir/LC_MESSAGES;
+ xgettext -d javascript -L Python --force-po --from-code utf-8 $join -c $(find ../../../../js/ -name \*.js)");
+}
diff --git a/tools/inc/cli.php b/tools/inc/cli.php
new file mode 100644
index 00000000..294609cd
--- /dev/null
+++ b/tools/inc/cli.php
@@ -0,0 +1,100 @@
+<?php
+
+/*
+ * This script will look for Tinyboard in the following places (in order):
+ * - $TINYBOARD_PATH environment varaible
+ * - ./
+ * - ./Tinyboard/
+ * - ../
+ */
+
+set_time_limit(0);
+$shell_path = getcwd();
+
+if(getenv('TINYBOARD_PATH') !== false)
+ $dir = getenv('TINYBOARD_PATH');
+elseif(file_exists('inc/functions.php'))
+ $dir = false;
+elseif(file_exists('Tinyboard') && is_dir('Tinyboard') && file_exists('Tinyboard/inc/functions.php'))
+ $dir = 'Tinyboard';
+elseif(file_exists('../inc/functions.php'))
+ $dir = '..';
+else
+ die("Could not locate Tinyboard directory!\n");
+
+if($dir && !chdir($dir))
+ die("Could not change directory to {$dir}\n");
+
+if(!getenv('TINYBOARD_PATH')) {
+ // follow symlink
+ chdir(realpath('inc') . '/..');
+}
+
+putenv('TINYBOARD_PATH=' . getcwd());
+
+require 'inc/functions.php';
+require 'inc/mod.php';
+
+$mod = Array(
+ 'id' => -1,
+ 'type' => ADMIN,
+ 'username' => '?',
+ 'boards' => Array('*')
+);
+
+function get_httpd_privileges() {
+ global $config, $shell_path, $argv;
+
+ if(php_sapi_name() != 'cli')
+ die("get_httpd_privileges(): invoked from HTTP client.\n");
+
+ echo "Dropping priviledges...\n";
+
+ if(!is_writable('.'))
+ die("get_httpd_privileges(): web directory is not writable\n");
+
+ $filename = '.' . md5(rand()) . '.php';
+ $inc_filename = '.' . md5(rand()) . '.php';
+
+ echo "Copying rebuilder to web directory...\n";
+
+ // replace "/inc/cli.php" with its new filename
+ passthru("cat " . escapeshellarg($shell_path . '/' . $_SERVER['PHP_SELF']) . " | sed \"s/'\/inc\/cli\.php'/'\/{$inc_filename}'/\" > {$filename}");
+
+ $inc_header = "<?php\n";
+
+ // copy environment
+ $env = explode("\n", shell_exec('printenv | grep ^TINYBOARD'));
+ foreach($env as $line) {
+ if(!empty($line))
+ $inc_header .= "putenv('" . addslashes($line) . "');\n";
+ }
+
+ // copy command line arguments
+ $inc_header .= "\$argv = " . var_export($argv, true) . ";\n";
+
+ // copy this file
+ file_put_contents($inc_filename, $inc_header . substr($inc = file_get_contents(__FILE__), strpos($inc, "\n")));
+
+ chmod($filename, 0666);
+ chmod($inc_filename, 0666);
+
+ if(preg_match('/^https?:\/\//', $config['root'])) {
+ $url = $config['root'] . $filename;
+ } elseif($host = getenv('TINYBOARD_HOST')) {
+ $url = 'http://' . $host . $config['root'] . $filename;
+ } else {
+ // assume localhost
+ $url = 'http://localhost' . $config['root'] . $filename;
+ }
+
+ echo "Downloading $url\n";
+
+ passthru('curl -s -N ' . escapeshellarg($url));
+
+ unlink($filename);
+ unlink($inc_filename);
+
+ exit(0);
+}
+
diff --git a/tools/inc/lib/jsgettext/JSParser.php b/tools/inc/lib/jsgettext/JSParser.php
new file mode 100644
index 00000000..adba59bc
--- /dev/null
+++ b/tools/inc/lib/jsgettext/JSParser.php
@@ -0,0 +1,94 @@
+<?php
+
+class JSParser {
+
+ protected $content;
+ protected $keywords;
+ protected $regs = array();
+ protected $regsCounter = 0;
+ protected $strings = array();
+ protected $stringsCounter = 0;
+
+ protected function _extractRegs($match) {
+ $this->regs[$this->regsCounter] = $match[1];
+ $id = "<<reg{$this->regsCounter}>>";
+ $this->regsCounter++;
+ return $id;
+ }
+ protected function _extractStrings($match) {
+ $this->strings[$this->stringsCounter] = $this->importRegExps($match[0]);
+ $id = "<<s{$this->stringsCounter}>>";
+ $this->stringsCounter++;
+ return $id;
+ }
+ protected function importRegExps($input) {
+ $regs = $this->regs;
+ return preg_replace_callback("#<<reg(\d+)>>#", function ($match) use($regs) {
+ return $regs[$match[1]];
+ }, $input);
+ }
+
+ protected function importStrings($input) {
+ $strings = $this->strings;
+ return preg_replace_callback("#<<s(\d+)>>#", function ($match) use($strings) {
+ return $strings[$match[1]];
+ }, $input);
+ }
+
+ public function __construct($file, $keywords = '_') {
+ $this->content = file_get_contents($file);
+ $this->keywords = (array)$keywords;
+ }
+
+ public function parse() {
+ $output = $this->content; //htmlspecialchars($this->content, ENT_NOQUOTES);
+
+ // extract reg exps
+ $output = preg_replace_callback(
+ '# ( / (?: (?>[^/\\\\]++) | \\\\\\\\ | (?<!\\\\)\\\\(?!\\\\) | \\\\/ )+ (?<!\\\\)/ ) [a-z]* \b #ix',
+ array($this, '_extractRegs'), $output
+ );
+
+ // extract strings
+ $output = preg_replace_callback(
+ array(
+ '# " ( (?: (?>[^"\\\\]++) | \\\\\\\\ | (?<!\\\\)\\\\(?!\\\\) | \\\\" )* ) (?<!\\\\)" #ix',
+ "# ' ( (?: (?>[^'\\\\]++) | \\\\\\\\ | (?<!\\\\)\\\\(?!\\\\) | \\\\' )* ) (?<!\\\\)' #ix"
+ ), array($this, '_extractStrings'), $output
+ );
+
+ // delete line comments
+ $output = preg_replace("#(//.*?)$#m", '', $output);
+
+ // delete multiline comments
+ $output = preg_replace('#/\*(.*?)\*/#is', '', $output);
+
+ $strings = $this->strings;
+ $output = preg_replace_callback("#<<s(\d+)>>#", function($match) use($strings) {
+ return $strings[$match[1]];
+ }, $output);
+
+ $keywords = implode('|', $this->keywords);
+
+ $strings = array();
+
+ // extract func calls
+ preg_match_all(
+ '# (?:'.$keywords.') \(\\ *" ( (?: (?>[^"\\\\]++) | \\\\\\\\ | (?<!\\\\)\\\\(?!\\\\) | \\\\" )* ) (?<!\\\\)"\\ *\) #ix',
+ $output, $matches, PREG_SET_ORDER
+ );
+
+ foreach ($matches as $m) $strings[] = stripslashes($m[1]);
+
+ $matches = array();
+ preg_match_all(
+ "# (?:$keywords) \(\\ *' ( (?: (?>[^'\\\\]++) | \\\\\\\\ | (?<!\\\\)\\\\(?!\\\\) | \\\\' )* ) (?<!\\\\)'\\ *\) #ix",
+ $output, $matches, PREG_SET_ORDER
+ );
+
+ foreach ($matches as $m) $strings[] = stripslashes($m[1]);
+
+ return $strings;
+ }
+}
+?>
diff --git a/tools/inc/lib/jsgettext/PoeditParser.php b/tools/inc/lib/jsgettext/PoeditParser.php
new file mode 100644
index 00000000..eebd816e
--- /dev/null
+++ b/tools/inc/lib/jsgettext/PoeditParser.php
@@ -0,0 +1,83 @@
+<?php
+
+require_once 'PoeditString.php';
+
+class PoeditParser {
+
+ protected $file;
+ protected $header = '';
+ protected $strings = array();
+
+ protected function _fixQuotes($str) {
+ return stripslashes($str);
+ }
+
+ public function __construct($file) {
+ $this->file = $file;
+ }
+
+ public function parse() {
+ $contents = file_get_contents($this->file);
+
+ $parts = preg_split('#(\r\n|\n){2}#', $contents, -1, PREG_SPLIT_NO_EMPTY);
+
+ $this->header = array_shift($parts);
+
+ foreach ($parts as $part) {
+
+ // parse comments
+ $comments = array();
+ preg_match_all('#^\\#: (.*?)$#m', $part, $matches, PREG_SET_ORDER);
+ foreach ($matches as $m) $comments[] = $m[1];
+
+ $isFuzzy = preg_match('#^\\#, fuzzy$#im', $part) ? true : false;
+
+ preg_match_all('# ^ (msgid|msgstr)\ " ( (?: (?>[^"\\\\]++) | \\\\\\\\ | (?<!\\\\)\\\\(?!\\\\) | \\\\" )* ) (?<!\\\\)" $ #ixm', $part, $matches2, PREG_SET_ORDER);
+
+ $k = $this->_fixQuotes($matches2[0][2]);
+ $v = !empty($matches2[1][2]) ? $this->_fixQuotes($matches2[1][2]) : '';
+
+ $this->strings[$k] = new PoeditString($k, $v, $isFuzzy, $comments);
+ }
+ }
+
+ public function merge($strings) {
+ foreach ((array)$strings as $str) {
+ if (!in_array($str, array_keys($this->strings))) {
+ $this->strings[$str] = new PoeditString($str);
+ }
+ }
+ }
+
+ public function getHeader() {
+ return $this->header;
+ }
+
+ public function getStrings() {
+ return $this->strings;
+ }
+
+ public function getJSON() {
+ $str = array();
+ foreach ($this->strings as $s) {
+ if ($s->value) $str[$s->key] = $s->value;
+ }
+ return json_encode($str);
+ }
+
+ public function toJSON($outputFilename, $varName = 'l10n') {
+ $str = "$varName = " . $this->getJSON() . ";";
+ return file_put_contents($outputFilename, $str) !== false;
+ }
+
+ public function save($filename = null) {
+ $data = $this->header . "\n\n";
+ foreach ($this->strings as $str) {
+ $data .= $str;
+ }
+ return file_put_contents($filename ? $filename : $this->file, $data) !== false;
+ }
+}
+
+
+?>
diff --git a/tools/inc/lib/jsgettext/PoeditString.php b/tools/inc/lib/jsgettext/PoeditString.php
new file mode 100644
index 00000000..3d59d004
--- /dev/null
+++ b/tools/inc/lib/jsgettext/PoeditString.php
@@ -0,0 +1,29 @@
+<?php
+
+class PoeditString {
+ public $key;
+ public $value;
+ public $fuzzy;
+ public $comments;
+
+ function __construct($key, $value = '', $fuzzy = false, $comments = array()) {
+ $this->key = $key;
+ $this->value = $value;
+ $this->fuzzy = $fuzzy;
+ $this->comments = (array)$comments;
+ }
+
+ public function __toString() {
+ $str ='';
+ foreach ($this->comments as $c) {
+ $str .= "#: $c\n";
+ }
+ if ($this->fuzzy) $str .= "#, fuzzy\n";
+ $str .= 'msgid "'.str_replace('"', '\\"', $this->key).'"' . "\n";
+ $str .= 'msgstr "'.str_replace('"', '\\"', $this->value).'"' . "\n";
+ $str .= "\n";
+ return $str;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tools/inc/lib/jsgettext/jsgettext.php b/tools/inc/lib/jsgettext/jsgettext.php
new file mode 100644
index 00000000..6c70f0b4
--- /dev/null
+++ b/tools/inc/lib/jsgettext/jsgettext.php
@@ -0,0 +1,63 @@
+<?php
+
+ require_once 'JSParser.php';
+ require_once 'PoeditParser.php';
+
+ function buildOptions($args) {
+ $options = array(
+ 'files' => array(),
+ '-o' => null,
+ '-k' => '_'
+ );
+ $len = count($args);
+ $i = 1;
+ while ($i < $len) {
+ if (preg_match('#^-[a-z]$#i', $args[$i])) {
+ $options[$args[$i]] = isset($args[$i+1]) ? trim($args[$i+1]) : true;
+ $i += 2;
+ }
+ else {
+ $options['files'][] = $args[$i];
+ $i++;
+ }
+ }
+ return $options;
+ }
+
+ $options = buildOptions($argv);
+
+ if (!file_exists($options['-o'])) {
+ touch($options['-o']);
+ }
+
+ if (!is_writable($options['-o'])) {
+ die("Invalid output file name. Make sure it exists and is writable.");
+ }
+
+ $inputFiles = $options['files'];
+
+ if (empty($inputFiles)) {
+ die("You did not provide any input file.");
+ }
+
+ $poeditParser = new PoeditParser($options['-o']);
+ $poeditParser->parse();
+
+ $errors = array();
+
+ foreach ($inputFiles as $f) {
+ if (!is_readable($f) || !preg_match('#\.js$#', $f)) {
+ $errors[] = ("$f is not a valid javascript file.");
+ continue;
+ }
+ $jsparser = new JSParser($f, explode(' ', $options['-k']));
+ $jsStrings = $jsparser->parse();
+ $poeditParser->merge($jsStrings);
+ }
+
+ if (!empty($errors)) {
+ echo "\nThe following errors occured:\n" . implode("\n", $errors) . "\n";
+ }
+
+ $poeditParser->save();
+?>
diff --git a/tools/inc/lib/jsgettext/po2json.php b/tools/inc/lib/jsgettext/po2json.php
new file mode 100644
index 00000000..847b0899
--- /dev/null
+++ b/tools/inc/lib/jsgettext/po2json.php
@@ -0,0 +1,42 @@
+<?php
+
+ require_once 'PoeditParser.php';
+
+ function buildOptions($args) {
+ $options = array(
+ '-o' => null,
+ '-i' => null,
+ '-n' => 'l10n'
+ );
+ $len = count($args);
+ $i = 0;
+ while ($i < $len) {
+ if (preg_match('#^-[a-z]$#i', $args[$i])) {
+ $options[$args[$i]] = isset($args[$i+1]) ? trim($args[$i+1]) : true;
+ $i += 2;
+ }
+ else {
+ $options[] = $args[$i];
+ $i++;
+ }
+ }
+ return $options;
+ }
+
+ $options = buildOptions($argv);
+
+ if (!file_exists($options['-i']) || !is_readable($options['-i'])) {
+ die("Invalid input file. Make sure it exists and is readable.");
+ }
+
+ $poeditParser = new PoeditParser($options['-i']);
+ $poeditParser->parse();
+
+ if ($poeditParser->toJSON($options['-o'], $options['-n'])) {
+ $strings = count($poeditParser->getStrings());
+ echo "Successfully exported " . count($strings) . " strings.\n";
+ }
+ else {
+ echo "Cannor write to file '{$options['-o']}'.\n";
+ }
+?>
diff --git a/tools/rebuild.php b/tools/rebuild.php
new file mode 100755
index 00000000..3b5c0de2
--- /dev/null
+++ b/tools/rebuild.php
@@ -0,0 +1,105 @@
+#!/usr/bin/php
+<?php
+
+/*
+ * rebuild.php - rebuilds all static files
+ *
+ * Command line arguments:
+ * -q, --quiet
+ * Suppress output.
+ *
+ * --quick
+ * Do not rebuild posts.
+ *
+ * -b, --board <string>
+ * Rebuild only the specified board.
+ *
+ * -f, --full
+ * Rebuild replies as well as threads (re-markup).
+ *
+ */
+
+require dirname(__FILE__) . '/inc/cli.php';
+
+if(!is_writable($config['file_script'])) {
+ get_httpd_privileges();
+}
+
+$start = microtime(true);
+
+// parse command line
+$opts = getopt('qfb:', Array('board:', 'quick', 'full', 'quiet'));
+$options = Array();
+
+$options['board'] = isset($opts['board']) ? $opts['board'] : (isset($opts['b']) ? $opts['b'] : false);
+$options['quiet'] = isset($opts['q']) || isset($opts['quiet']);
+$options['quick'] = isset($opts['quick']);
+$options['full'] = isset($opts['full']) || isset($opts['f']);
+
+if(!$options['quiet'])
+ echo "== Tinyboard {$config['version']} ==\n";
+
+if(!$options['quiet'])
+ echo "Clearing template cache...\n";
+
+load_twig();
+$twig->clearCacheFiles();
+
+if(!$options['quiet'])
+ echo "Regenerating theme files...\n";
+rebuildThemes('all');
+
+if(!$options['quiet'])
+ echo "Generating Javascript file...\n";
+buildJavascript();
+
+$main_js = $config['file_script'];
+
+$boards = listBoards();
+
+foreach($boards as &$board) {
+ if($options['board'] && $board['uri'] != $options['board'])
+ continue;
+
+ if(!$options['quiet'])
+ echo "Opening board /{$board['uri']}/...\n";
+ openBoard($board['uri']);
+
+ if($config['file_script'] != $main_js) {
+ // different javascript file
+ if(!$options['quiet'])
+ echo "Generating Javascript file...\n";
+ buildJavascript();
+ }
+
+
+ if(!$options['quiet'])
+ echo "Creating index pages...\n";
+ buildIndex();
+
+ if($options['quick'])
+ continue; // do no more
+
+ if($options['full']) {
+ $query = query(sprintf("SELECT `id` FROM `posts_%s`", $board['uri'])) or error(db_error());
+ while($post = $query->fetch()) {
+ if(!$options['quiet'])
+ echo "Rebuilding #{$post['id']}...\n";
+ rebuildPost($post['id']);
+ }
+ }
+
+ $query = query(sprintf("SELECT `id` FROM `posts_%s` WHERE `thread` IS NULL", $board['uri'])) or error(db_error());
+ while($post = $query->fetch()) {
+ if(!$options['quiet'])
+ echo "Rebuilding #{$post['id']}...\n";
+ buildThread($post['id']);
+ }
+}
+
+if(!$options['quiet'])
+ printf("Complete! Took %g seconds\n", microtime(true) - $start);
+
+unset($board);
+modLog('Rebuilt everything using tools/rebuild.php');
+