commit
98cb2725eb
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,3 +1,9 @@
|
||||||
[submodule "js/wPaint"]
|
[submodule "js/wPaint"]
|
||||||
path = js/wPaint
|
path = js/wPaint
|
||||||
url = https://github.com/vichan-devel/wPaint.git
|
url = https://github.com/vichan-devel/wPaint.git
|
||||||
|
branch = master
|
||||||
|
|
||||||
|
[submodule "inc/lib/parsedown"]
|
||||||
|
path = inc/lib/parsedown
|
||||||
|
url = https://github.com/vichan-devel/parsedown
|
||||||
|
branch = master
|
||||||
|
|
|
@ -55,7 +55,7 @@ Installation
|
||||||
|
|
||||||
Please remember to change the administrator account password.
|
Please remember to change the administrator account password.
|
||||||
|
|
||||||
See also: [Configuration Basics](http://tinyboard.org/docs/?p=Config).
|
See also: [Configuration Basics](https://web.archive.org/web/20121003095922/http://tinyboard.org/docs/?p=Config).
|
||||||
|
|
||||||
Upgrade
|
Upgrade
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Api {
|
||||||
'images' => 'images',
|
'images' => 'images',
|
||||||
'sticky' => 'sticky',
|
'sticky' => 'sticky',
|
||||||
'locked' => 'locked',
|
'locked' => 'locked',
|
||||||
|
'cycle' => 'cyclical',
|
||||||
'bump' => 'last_modified',
|
'bump' => 'last_modified',
|
||||||
'embed' => 'embed',
|
'embed' => 'embed',
|
||||||
);
|
);
|
||||||
|
@ -92,8 +93,13 @@ class Api {
|
||||||
$dotPos = strrpos($file->file, '.');
|
$dotPos = strrpos($file->file, '.');
|
||||||
$apiPost['ext'] = substr($file->file, $dotPos);
|
$apiPost['ext'] = substr($file->file, $dotPos);
|
||||||
$apiPost['tim'] = substr($file->file, 0, $dotPos);
|
$apiPost['tim'] = substr($file->file, 0, $dotPos);
|
||||||
|
if (isset ($file->hash) && $file->hash) {
|
||||||
|
$apiPost['md5'] = base64_encode(hex2bin($file->hash));
|
||||||
|
}
|
||||||
|
else if (isset ($post->filehash) && $post->filehash) {
|
||||||
$apiPost['md5'] = base64_encode(hex2bin($post->filehash));
|
$apiPost['md5'] = base64_encode(hex2bin($post->filehash));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function translatePost($post, $threadsPage = false) {
|
private function translatePost($post, $threadsPage = false) {
|
||||||
global $config, $board;
|
global $config, $board;
|
||||||
|
|
|
@ -166,7 +166,7 @@ class Bans {
|
||||||
|
|
||||||
if ($ban['post']) {
|
if ($ban['post']) {
|
||||||
$post = json_decode($ban['post']);
|
$post = json_decode($ban['post']);
|
||||||
$ban['message'] = $post->body;
|
$ban['message'] = isset($post->body) ? $post->body : 0;
|
||||||
}
|
}
|
||||||
unset($ban['ipstart'], $ban['ipend'], $ban['post'], $ban['creator']);
|
unset($ban['ipstart'], $ban['ipend'], $ban['post'], $ban['creator']);
|
||||||
|
|
||||||
|
|
357
inc/captcha/captcha.php
Normal file
357
inc/captcha/captcha.php
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
<?php
|
||||||
|
class CzaksCaptcha {
|
||||||
|
var $content = array();
|
||||||
|
|
||||||
|
var $width, $height, $color, $charset, $style;
|
||||||
|
|
||||||
|
function __construct($text, $left, $top, $charset=false) {
|
||||||
|
if (!$charset) {
|
||||||
|
$charset = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
}
|
||||||
|
|
||||||
|
$len = mb_strlen($text, 'utf-8');
|
||||||
|
|
||||||
|
$this->width = $left;
|
||||||
|
$this->height = $top;
|
||||||
|
|
||||||
|
$this->charset = preg_split('//u', $charset);
|
||||||
|
|
||||||
|
$this->style = "";
|
||||||
|
|
||||||
|
for ($i = 0; $i < $len; $i++) {
|
||||||
|
$this->content[] = array(mb_substr($text, $i, 1, 'utf-8'), "top" => $top / 2 - $top / 4,
|
||||||
|
"left" => $left/10 + 9*$left*$i/10/$len,
|
||||||
|
"position" => "absolute");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->color = "hsla(".rand(1,360).", 76%, 78%, 1)";
|
||||||
|
|
||||||
|
$this->add_junk();
|
||||||
|
$this->mutate_sizes();
|
||||||
|
$this->mutate_positions();
|
||||||
|
$this->mutate_transform();
|
||||||
|
$this->mutate_anchors();
|
||||||
|
$this->randomize();
|
||||||
|
$this->mutate_containers();
|
||||||
|
$this->mutate_margins();
|
||||||
|
$this->mutate_styles();
|
||||||
|
$this->randomize();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutate_sizes() {
|
||||||
|
foreach ($this->content as &$v) {
|
||||||
|
if (!isset ($v['font-size']))
|
||||||
|
$v['font-size'] = rand($this->height/3 - 4, $this->height/3 + 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function mutate_positions() {
|
||||||
|
foreach ($this->content as &$v) {
|
||||||
|
$v['top'] += rand(-10,10);
|
||||||
|
$v['left'] += rand(-10,10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function mutate_transform() {
|
||||||
|
$fromto = array('6'=>'9', '9'=>'6', '8'=>'8', '0'=>'0',
|
||||||
|
'z'=>'z', 's'=>'s', 'n'=>'u', 'u'=>'n',
|
||||||
|
'a'=>'ɐ', 'e'=>'ə', 'p'=>'d', 'd'=>'p',
|
||||||
|
'A'=>'∀', 'E'=>'∃', 'H'=>'H', 'o'=>'o',
|
||||||
|
'O'=>'O');
|
||||||
|
|
||||||
|
foreach ($this->content as &$v) {
|
||||||
|
$basefrom = -20;
|
||||||
|
$baseto = 20;
|
||||||
|
|
||||||
|
if (isset($fromto[$v[0]]) && rand(0,1)) {
|
||||||
|
$v[0] = $fromto[$v[0]];
|
||||||
|
$basefrom = 160;
|
||||||
|
$baseto = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
$v['transform'] = 'rotate('.rand($basefrom,$baseto).'deg)';
|
||||||
|
$v['-ms-transform'] = 'rotate('.rand($basefrom,$baseto).'deg)';
|
||||||
|
$v['-webkit-transform'] = 'rotate('.rand($basefrom,$baseto).'deg)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function randomize(&$a = false) {
|
||||||
|
if ($a === false) {
|
||||||
|
$a = &$this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffle($a);
|
||||||
|
|
||||||
|
foreach ($a as &$v) {
|
||||||
|
$this->shuffle_assoc($v);
|
||||||
|
|
||||||
|
if (is_array ($v[0])) {
|
||||||
|
$this->randomize($v[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_junk() {
|
||||||
|
$count = rand(200, 300);
|
||||||
|
|
||||||
|
while ($count--) {
|
||||||
|
$elem = array();
|
||||||
|
|
||||||
|
$elem['top'] = rand(0, $this->height);
|
||||||
|
$elem['left'] = rand(0, $this->width);
|
||||||
|
|
||||||
|
$elem['position'] = 'absolute';
|
||||||
|
|
||||||
|
$elem[0] = $this->charset[rand(0, count($this->charset)-1)];
|
||||||
|
|
||||||
|
switch($t = rand (0,9)) {
|
||||||
|
case 0:
|
||||||
|
$elem['display'] = 'none'; break;
|
||||||
|
case 1:
|
||||||
|
$elem['top'] = rand(-60, -90); break;
|
||||||
|
case 2:
|
||||||
|
$elem['left'] = rand(-40, -70); break;
|
||||||
|
case 3:
|
||||||
|
$elem['top'] = $this->height + rand(10, 60); break;
|
||||||
|
case 4:
|
||||||
|
$elem['left'] = $this->width + rand(10, 60); break;
|
||||||
|
case 5:
|
||||||
|
$elem['color'] = $this->color; break;
|
||||||
|
case 6:
|
||||||
|
$elem['visibility'] = 'hidden'; break;
|
||||||
|
case 7:
|
||||||
|
$elem['height'] = rand(0,2);
|
||||||
|
$elem['overflow'] = 'hidden'; break;
|
||||||
|
case 8:
|
||||||
|
$elem['width'] = rand(0,1);
|
||||||
|
$elem['overflow'] = 'hidden'; break;
|
||||||
|
case 9:
|
||||||
|
$elem['font-size'] = rand(2, 6); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->content[] = $elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutate_anchors() {
|
||||||
|
foreach ($this->content as &$elem) {
|
||||||
|
if (rand(0,1)) {
|
||||||
|
$elem['right'] = $this->width - $elem['left'] - (int)(0.5*$elem['font-size']);
|
||||||
|
unset($elem['left']);
|
||||||
|
}
|
||||||
|
if (rand(0,1)) {
|
||||||
|
$elem['bottom'] = $this->height - $elem['top'] - (int)(1.5*$elem['font-size']);
|
||||||
|
unset($elem['top']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutate_containers() {
|
||||||
|
for ($i = 0; $i <= 80; $i++) {
|
||||||
|
$new = [];
|
||||||
|
$new['width'] = rand(0, $this->width*2);
|
||||||
|
$new['height'] = rand(0, $this->height*2);
|
||||||
|
$new['top'] = rand(-$this->height * 2, $this->height * 2);
|
||||||
|
$new['bottom'] = $this->height - ($new['top'] + $new['height']);
|
||||||
|
$new['left'] = rand(-$this->width * 2, $this->width * 2);
|
||||||
|
$new['right'] = $this->width - ($new['left'] + $new['width']);
|
||||||
|
|
||||||
|
$new['position'] = 'absolute';
|
||||||
|
|
||||||
|
$new[0] = [];
|
||||||
|
|
||||||
|
$cnt = rand(0,10);
|
||||||
|
for ($j = 0; $j < $cnt; $j++) {
|
||||||
|
$elem = array_pop($this->content);
|
||||||
|
if (!$elem) break;
|
||||||
|
|
||||||
|
if (isset($elem['top'])) $elem['top'] -= $new['top'];
|
||||||
|
if (isset($elem['bottom'])) $elem['bottom'] -= $new['bottom'];
|
||||||
|
if (isset($elem['left'])) $elem['left'] -= $new['left'];
|
||||||
|
if (isset($elem['right'])) $elem['right'] -= $new['right'];
|
||||||
|
|
||||||
|
$new[0][] = $elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rand (0,1)) unset($new['top']);
|
||||||
|
else unset($new['bottom']);
|
||||||
|
if (rand (0,1)) unset($new['left']);
|
||||||
|
else unset($new['right']);
|
||||||
|
|
||||||
|
$this->content[] = $new;
|
||||||
|
|
||||||
|
shuffle($this->content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutate_margins(&$a = false) {
|
||||||
|
if ($a === false) {
|
||||||
|
$a = &$this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($a as &$v) {
|
||||||
|
$ary = ['top', 'left', 'bottom', 'right'];
|
||||||
|
shuffle($ary);
|
||||||
|
$cnt = rand(0,4);
|
||||||
|
$ary = array_slice($ary, 0, $cnt);
|
||||||
|
|
||||||
|
foreach ($ary as $prop) {
|
||||||
|
$margin = rand(-1000, 1000);
|
||||||
|
|
||||||
|
$v['margin-'.$prop] = $margin;
|
||||||
|
|
||||||
|
if (isset($v[$prop])) {
|
||||||
|
$v[$prop] -= $margin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($v[0])) {
|
||||||
|
$this->mutate_margins($v[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutate_styles(&$a = false) {
|
||||||
|
if ($a === false) {
|
||||||
|
$a = &$this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($a as &$v) {
|
||||||
|
$content = $v[0];
|
||||||
|
unset($v[0]);
|
||||||
|
$styles = array_splice($v, 0, rand(0, 6));
|
||||||
|
$v[0] = $content;
|
||||||
|
|
||||||
|
$id_or_class = rand(0,1);
|
||||||
|
$param = $id_or_class ? "id" : "class";
|
||||||
|
$prefix = $id_or_class ? "#" : ".";
|
||||||
|
$genname = "zz-".base_convert(rand(1,999999999), 10, 36);
|
||||||
|
|
||||||
|
if ($styles || rand(0,1)) {
|
||||||
|
$this->style .= $prefix.$genname."{";
|
||||||
|
$this->style .= $this->rand_whitespace();
|
||||||
|
|
||||||
|
foreach ($styles as $k => $val) {
|
||||||
|
if (is_int($val)) {
|
||||||
|
$val = "".$val."px";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->style .= "$k:";
|
||||||
|
$this->style .= $this->rand_whitespace();
|
||||||
|
$this->style .= "$val;";
|
||||||
|
$this->style .= $this->rand_whitespace();
|
||||||
|
}
|
||||||
|
$this->style .= "}";
|
||||||
|
$this->style .= $this->rand_whitespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
$v[$param] = $genname;
|
||||||
|
|
||||||
|
if (is_array($v[0])) {
|
||||||
|
$this->mutate_styles($v[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_html(&$a = false) {
|
||||||
|
$inside = true;
|
||||||
|
|
||||||
|
if ($a === false) {
|
||||||
|
if ($this->style) {
|
||||||
|
echo "<style type='text/css'>";
|
||||||
|
echo $this->style;
|
||||||
|
echo "</style>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<div style='position: relative; width: ".$this->width."px; height: ".$this->height."px; overflow: hidden; background-color: ".$this->color."'>";
|
||||||
|
$a = &$this->content;
|
||||||
|
$inside = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($a as &$v) {
|
||||||
|
$letter = $v[0];
|
||||||
|
|
||||||
|
unset ($v[0]);
|
||||||
|
|
||||||
|
echo "<div";
|
||||||
|
echo $this->rand_whitespace(1);
|
||||||
|
|
||||||
|
if (isset ($v['id'])) {
|
||||||
|
echo "id='$v[id]'";
|
||||||
|
echo $this->rand_whitespace(1);
|
||||||
|
|
||||||
|
unset ($v['id']);
|
||||||
|
}
|
||||||
|
if (isset ($v['class'])) {
|
||||||
|
echo "class='$v[class]'";
|
||||||
|
echo $this->rand_whitespace(1);
|
||||||
|
|
||||||
|
unset ($v['class']);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "style='";
|
||||||
|
|
||||||
|
foreach ($v as $k => $val) {
|
||||||
|
if (is_int($val)) {
|
||||||
|
$val = "".$val."px";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "$k:";
|
||||||
|
echo $this->rand_whitespace();
|
||||||
|
echo "$val;";
|
||||||
|
echo $this->rand_whitespace();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "'>";
|
||||||
|
echo $this->rand_whitespace();
|
||||||
|
|
||||||
|
if (is_array ($letter)) {
|
||||||
|
$this->to_html($letter);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo $letter;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$inside) {
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function rand_whitespace($r = 0) {
|
||||||
|
switch (rand($r,4)) {
|
||||||
|
case 0:
|
||||||
|
return "";
|
||||||
|
case 1:
|
||||||
|
return "\n";
|
||||||
|
case 2:
|
||||||
|
return "\t";
|
||||||
|
case 3:
|
||||||
|
return " ";
|
||||||
|
case 4:
|
||||||
|
return " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function shuffle_assoc(&$array) {
|
||||||
|
$keys = array_keys($array);
|
||||||
|
|
||||||
|
shuffle($keys);
|
||||||
|
|
||||||
|
foreach($keys as $key) {
|
||||||
|
$new[$key] = $array[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
$array = $new;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//$charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789卐";
|
||||||
|
|
||||||
|
//(new CzaksCaptcha("hotwheels", 300, 80, $charset))->to_html();
|
||||||
|
?>
|
16
inc/captcha/config.php
Normal file
16
inc/captcha/config.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
// We are using a custom path here to connect to the database.
|
||||||
|
// Why? Performance reasons.
|
||||||
|
|
||||||
|
$pdo = new PDO("mysql:dbname=database_name;host=localhost", "database_user", "database_password", array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
|
||||||
|
|
||||||
|
|
||||||
|
// Captcha expiration:
|
||||||
|
$expires_in = 120; // 120 seconds
|
||||||
|
|
||||||
|
// Captcha dimensions:
|
||||||
|
$width = 250;
|
||||||
|
$height = 80;
|
||||||
|
|
||||||
|
// Captcha length:
|
||||||
|
$length = 6;
|
9
inc/captcha/dbschema.sql
Normal file
9
inc/captcha/dbschema.sql
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
SET NAMES utf8;
|
||||||
|
|
||||||
|
CREATE TABLE `captchas` (
|
||||||
|
`cookie` VARCHAR(50),
|
||||||
|
`extra` VARCHAR(200),
|
||||||
|
`text` VARCHAR(255),
|
||||||
|
`created_at` INT(11),
|
||||||
|
PRIMARY KEY (cookie, extra)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
85
inc/captcha/entrypoint.php
Normal file
85
inc/captcha/entrypoint.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
$mode = @$_GET['mode'];
|
||||||
|
|
||||||
|
require_once("captcha.php");
|
||||||
|
|
||||||
|
function rand_string($length, $charset) {
|
||||||
|
$ret = "";
|
||||||
|
while ($length--) {
|
||||||
|
$ret .= mb_substr($charset, rand(0, mb_strlen($charset, 'utf-8')-1), 1, 'utf-8');
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup ($pdo, $expires_in) {
|
||||||
|
$pdo->prepare("DELETE FROM `captchas` WHERE `created_at` < ?")->execute([time() - $expires_in]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($mode) {
|
||||||
|
// Request: GET entrypoint.php?mode=get&extra=1234567890
|
||||||
|
// Response: JSON: cookie => "generatedcookie", captchahtml => "captchahtml", expires_in => 120
|
||||||
|
case "get":
|
||||||
|
if (!isset ($_GET['extra'])) {
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Content-type: application/json");
|
||||||
|
|
||||||
|
$extra = $_GET['extra'];
|
||||||
|
|
||||||
|
require_once("config.php");
|
||||||
|
|
||||||
|
$text = rand_string($length, $extra);
|
||||||
|
|
||||||
|
$captcha = new CzaksCaptcha($text, $width, $height, $extra);
|
||||||
|
|
||||||
|
$cookie = rand_string(20, "abcdefghijklmnopqrstuvwxyz");
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$captcha->to_html();
|
||||||
|
$html = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
$query = $pdo->prepare("INSERT INTO `captchas` (`cookie`, `extra`, `text`, `created_at`) VALUES (?, ?, ?, ?)");
|
||||||
|
$query->execute( [$cookie, $extra, $text, time()]);
|
||||||
|
|
||||||
|
echo json_encode(["cookie" => $cookie, "captchahtml" => $html, "expires_in" => $expires_in]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Request: GET entrypoint.php?mode=check&cookie=generatedcookie&extra=1234567890&text=captcha
|
||||||
|
// Response: 0 OR 1
|
||||||
|
case "check":
|
||||||
|
if (!isset ($_GET['mode'])
|
||||||
|
|| !isset ($_GET['cookie'])
|
||||||
|
|| !isset ($_GET['extra'])
|
||||||
|
|| !isset ($_GET['text'])) {
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once("config.php");
|
||||||
|
|
||||||
|
cleanup($pdo, $expires_in);
|
||||||
|
|
||||||
|
$query = $pdo->prepare("SELECT * FROM `captchas` WHERE `cookie` = ? AND `extra` = ?");
|
||||||
|
$query->execute([$_GET['cookie'], $_GET['extra']]);
|
||||||
|
|
||||||
|
$ary = $query->fetchAll();
|
||||||
|
|
||||||
|
if (!$ary) {
|
||||||
|
echo "0";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$query = $pdo->prepare("DELETE FROM `captchas` WHERE `cookie` = ? AND `extra` = ?");
|
||||||
|
$query->execute([$_GET['cookie'], $_GET['extra']]);
|
||||||
|
|
||||||
|
if ($ary[0]['text'] !== $_GET['text']) {
|
||||||
|
echo "0";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
10
inc/captcha/readme.md
Normal file
10
inc/captcha/readme.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
I integrated this from: https://github.com/ctrlcctrlv/infinity/commit/62a6dac022cb338f7b719d0c35a64ab3efc64658
|
||||||
|
|
||||||
|
<strike>First import the captcha/dbschema.sql in your database</strike> it is no longer required.
|
||||||
|
|
||||||
|
In inc/captcha/config.php change the database_name database_user database_password to your own settings.
|
||||||
|
|
||||||
|
Add js/captcha.js in your instance-config.php or config.php
|
||||||
|
|
||||||
|
Go to Line 305 in the /inc/config file and copy the settings in instance config, while changing the url to your website.
|
||||||
|
Go to the line beneath it if you only want to enable it when posting a new thread.
|
209
inc/config.php
209
inc/config.php
|
@ -103,7 +103,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
* Cache settings
|
* Cache, lock and queue settings
|
||||||
* ====================
|
* ====================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -120,6 +120,7 @@
|
||||||
// $config['cache']['enabled'] = 'apc';
|
// $config['cache']['enabled'] = 'apc';
|
||||||
// $config['cache']['enabled'] = 'memcached';
|
// $config['cache']['enabled'] = 'memcached';
|
||||||
// $config['cache']['enabled'] = 'redis';
|
// $config['cache']['enabled'] = 'redis';
|
||||||
|
// $config['cache']['enabled'] = 'fs';
|
||||||
|
|
||||||
// Timeout for cached objects such as posts and HTML.
|
// Timeout for cached objects such as posts and HTML.
|
||||||
$config['cache']['timeout'] = 60 * 60 * 48; // 48 hours
|
$config['cache']['timeout'] = 60 * 60 * 48; // 48 hours
|
||||||
|
@ -142,6 +143,12 @@
|
||||||
// (this file will be explicitly loaded during cache hit, but not during cache miss).
|
// (this file will be explicitly loaded during cache hit, but not during cache miss).
|
||||||
$config['cache_config'] = false;
|
$config['cache_config'] = false;
|
||||||
|
|
||||||
|
// Define a lock driver.
|
||||||
|
$config['lock']['enabled'] = 'fs';
|
||||||
|
|
||||||
|
// Define a queue driver.
|
||||||
|
$config['queue']['enabled'] = 'fs'; // xD
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
* Cookie settings
|
* Cookie settings
|
||||||
|
@ -189,7 +196,11 @@
|
||||||
|
|
||||||
// Prevents most Tor exit nodes from making posts. Recommended, as a lot of abuse comes from Tor because
|
// Prevents most Tor exit nodes from making posts. Recommended, as a lot of abuse comes from Tor because
|
||||||
// of the strong anonymity associated with it.
|
// of the strong anonymity associated with it.
|
||||||
$config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1);
|
// Example: $config['dnsbl'][] = 'another.blacklist.net'; //
|
||||||
|
// $config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1); //sectoor.de site is dead. the number stands for (an) ip adress(es) I guess.
|
||||||
|
|
||||||
|
// Replacement for sectoor.de
|
||||||
|
$config['dnsbl'][] = array('rbl.efnet.org', 4);
|
||||||
|
|
||||||
// http://www.sorbs.net/using.shtml
|
// http://www.sorbs.net/using.shtml
|
||||||
// $config['dnsbl'][] = array('dnsbl.sorbs.net', array(2, 3, 4, 5, 6, 7, 8, 9));
|
// $config['dnsbl'][] = array('dnsbl.sorbs.net', array(2, 3, 4, 5, 6, 7, 8, 9));
|
||||||
|
@ -275,8 +286,9 @@
|
||||||
'lock',
|
'lock',
|
||||||
'raw',
|
'raw',
|
||||||
'embed',
|
'embed',
|
||||||
'recaptcha_challenge_field',
|
'g-recaptcha-response',
|
||||||
'recaptcha_response_field',
|
'captcha_cookie',
|
||||||
|
'captcha_text',
|
||||||
'spoiler',
|
'spoiler',
|
||||||
'page',
|
'page',
|
||||||
'file_url',
|
'file_url',
|
||||||
|
@ -293,6 +305,26 @@
|
||||||
$config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f';
|
$config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f';
|
||||||
$config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_';
|
$config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_';
|
||||||
|
|
||||||
|
// Enable Custom Captcha you need to change a couple of settings
|
||||||
|
//Read more at: /captcha/instructions.md
|
||||||
|
$config['captcha'] = array();
|
||||||
|
|
||||||
|
// Enable custom captcha provider
|
||||||
|
$config['captcha']['enabled'] = false;
|
||||||
|
|
||||||
|
//New thread captcha
|
||||||
|
//Require solving a captcha to post a thread.
|
||||||
|
//Default off.
|
||||||
|
$config['new_thread_capt'] = false;
|
||||||
|
|
||||||
|
// Custom captcha get provider path (if not working get the absolute path aka your url.)
|
||||||
|
$config['captcha']['provider_get'] = '../inc/captcha/entrypoint.php';
|
||||||
|
// Custom captcha check provider path
|
||||||
|
$config['captcha']['provider_check'] = '../inc/captcha/entrypoint.php';
|
||||||
|
|
||||||
|
// Custom captcha extra field (eg. charset)
|
||||||
|
$config['captcha']['extra'] = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
|
||||||
// Ability to lock a board for normal users and still allow mods to post. Could also be useful for making an archive board
|
// Ability to lock a board for normal users and still allow mods to post. Could also be useful for making an archive board
|
||||||
$config['board_locked'] = false;
|
$config['board_locked'] = false;
|
||||||
|
|
||||||
|
@ -511,6 +543,14 @@
|
||||||
// The timeout for the above, in seconds.
|
// The timeout for the above, in seconds.
|
||||||
$config['upload_by_url_timeout'] = 15;
|
$config['upload_by_url_timeout'] = 15;
|
||||||
|
|
||||||
|
// Enable early 404? With default settings, a thread would 404 if it was to leave page 3, if it had less
|
||||||
|
// than 3 replies.
|
||||||
|
$config['early_404'] = false;
|
||||||
|
|
||||||
|
$config['early_404_page'] = 3;
|
||||||
|
$config['early_404_replies'] = 5;
|
||||||
|
$config['early_404_staged'] = false;
|
||||||
|
|
||||||
// A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts
|
// A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts
|
||||||
// as they are submitted and changes or censors particular words or phrases.
|
// as they are submitted and changes or censors particular words or phrases.
|
||||||
|
|
||||||
|
@ -650,7 +690,7 @@
|
||||||
*/
|
*/
|
||||||
// Maximum number of images allowed. Increasing this number enabled multi image.
|
// Maximum number of images allowed. Increasing this number enabled multi image.
|
||||||
// If you make it more than 1, make sure to enable the below script for the post form to change.
|
// If you make it more than 1, make sure to enable the below script for the post form to change.
|
||||||
// $config['additional_javascript'][] = 'js/multi_image.js';
|
// $config['additional_javascript'][] = 'js/multi-image.js';
|
||||||
$config['max_images'] = 1;
|
$config['max_images'] = 1;
|
||||||
|
|
||||||
// Method to use for determing the max filesize.
|
// Method to use for determing the max filesize.
|
||||||
|
@ -766,7 +806,7 @@
|
||||||
// Location of thumbnail to use for spoiler images.
|
// Location of thumbnail to use for spoiler images.
|
||||||
$config['spoiler_image'] = 'static/spoiler.png';
|
$config['spoiler_image'] = 'static/spoiler.png';
|
||||||
// Location of thumbnail to use for deleted images.
|
// Location of thumbnail to use for deleted images.
|
||||||
// $config['image_deleted'] = 'static/deleted.png';
|
$config['image_deleted'] = 'static/deleted.png';
|
||||||
|
|
||||||
// When a thumbnailed image is going to be the same (in dimension), just copy the entire file and use
|
// When a thumbnailed image is going to be the same (in dimension), just copy the entire file and use
|
||||||
// that as a thumbnail instead of resizing/redrawing.
|
// that as a thumbnail instead of resizing/redrawing.
|
||||||
|
@ -807,8 +847,17 @@
|
||||||
// Set this to true if you're using a BSD
|
// Set this to true if you're using a BSD
|
||||||
$config['bsd_md5'] = false;
|
$config['bsd_md5'] = false;
|
||||||
|
|
||||||
// Set this to true if you're having problems with image duplicated error and bsd_md5 doesn't help.
|
// Set this to true if you're using Linux and you can execute `md5sum` binary.
|
||||||
$config['php_md5'] = false;
|
$config['gnu_md5'] = false;
|
||||||
|
|
||||||
|
// Use Tesseract OCR to retrieve text from images, so you can use it as a spamfilter.
|
||||||
|
$config['tesseract_ocr'] = false;
|
||||||
|
|
||||||
|
// Tesseract parameters
|
||||||
|
$config['tesseract_params'] = '';
|
||||||
|
|
||||||
|
// Tesseract preprocess command
|
||||||
|
$config['tesseract_preprocess_command'] = 'convert -monochrome %s -';
|
||||||
|
|
||||||
// Number of posts in a "View Last X Posts" page
|
// Number of posts in a "View Last X Posts" page
|
||||||
$config['noko50_count'] = 50;
|
$config['noko50_count'] = 50;
|
||||||
|
@ -931,8 +980,8 @@
|
||||||
// Show page navigation links at the top as well.
|
// Show page navigation links at the top as well.
|
||||||
$config['page_nav_top'] = false;
|
$config['page_nav_top'] = false;
|
||||||
|
|
||||||
// Show "Catalog" link in page navigation. Use with the Catalog theme.
|
// Show "Catalog" link in page navigation. Use with the Catalog theme. Set to false to disable.
|
||||||
// $config['catalog_link'] = 'catalog.html';
|
$config['catalog_link'] = 'catalog.html';
|
||||||
|
|
||||||
// Board categories. Only used in the "Categories" theme.
|
// Board categories. Only used in the "Categories" theme.
|
||||||
// $config['categories'] = array(
|
// $config['categories'] = array(
|
||||||
|
@ -978,6 +1027,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Additional Javascript files to include on board index and thread pages. See js/ for available scripts.
|
// Additional Javascript files to include on board index and thread pages. See js/ for available scripts.
|
||||||
|
$config['additional_javascript'][] = 'js/jquery.min.js';
|
||||||
$config['additional_javascript'][] = 'js/inline-expanding.js';
|
$config['additional_javascript'][] = 'js/inline-expanding.js';
|
||||||
// $config['additional_javascript'][] = 'js/local-time.js';
|
// $config['additional_javascript'][] = 'js/local-time.js';
|
||||||
|
|
||||||
|
@ -991,6 +1041,7 @@
|
||||||
// $config['additional_javascript'][] = 'js/auto-reload.js';
|
// $config['additional_javascript'][] = 'js/auto-reload.js';
|
||||||
// $config['additional_javascript'][] = 'js/post-hover.js';
|
// $config['additional_javascript'][] = 'js/post-hover.js';
|
||||||
// $config['additional_javascript'][] = 'js/style-select.js';
|
// $config['additional_javascript'][] = 'js/style-select.js';
|
||||||
|
// $config['additional_javascript'][] = 'js/captcha.js';
|
||||||
|
|
||||||
// Where these script files are located on the web. Defaults to $config['root'].
|
// Where these script files are located on the web. Defaults to $config['root'].
|
||||||
// $config['additional_javascript_url'] = 'http://static.example.org/tinyboard-javascript-stuff/';
|
// $config['additional_javascript_url'] = 'http://static.example.org/tinyboard-javascript-stuff/';
|
||||||
|
@ -1001,6 +1052,10 @@
|
||||||
// Minify Javascript using http://code.google.com/p/minify/.
|
// Minify Javascript using http://code.google.com/p/minify/.
|
||||||
$config['minify_js'] = false;
|
$config['minify_js'] = false;
|
||||||
|
|
||||||
|
// Dispatch thumbnail loading and image configuration with JavaScript. It will need a certain javascript
|
||||||
|
// code to work.
|
||||||
|
$config['javascript_image_dispatch'] = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
* Video embedding
|
* Video embedding
|
||||||
|
@ -1076,6 +1131,7 @@
|
||||||
$config['error']['toomanycross'] = _('Too many cross-board links; post discarded.');
|
$config['error']['toomanycross'] = _('Too many cross-board links; post discarded.');
|
||||||
$config['error']['nodelete'] = _('You didn\'t select anything to delete.');
|
$config['error']['nodelete'] = _('You didn\'t select anything to delete.');
|
||||||
$config['error']['noreport'] = _('You didn\'t select anything to report.');
|
$config['error']['noreport'] = _('You didn\'t select anything to report.');
|
||||||
|
$config['error']['invalidreport'] = _('The reason was too long.');
|
||||||
$config['error']['toomanyreports'] = _('You can\'t report that many posts at once.');
|
$config['error']['toomanyreports'] = _('You can\'t report that many posts at once.');
|
||||||
$config['error']['invalidpassword'] = _('Wrong password…');
|
$config['error']['invalidpassword'] = _('Wrong password…');
|
||||||
$config['error']['invalidimg'] = _('Invalid image.');
|
$config['error']['invalidimg'] = _('Invalid image.');
|
||||||
|
@ -1196,16 +1252,74 @@
|
||||||
// Try not to build pages when we shouldn't have to.
|
// Try not to build pages when we shouldn't have to.
|
||||||
$config['try_smarter'] = true;
|
$config['try_smarter'] = true;
|
||||||
|
|
||||||
// EXPERIMENTAL: Defer static HTML building to a moment, when a given file is actually accessed.
|
/*
|
||||||
// Warning: This option won't run out of the box. You need to tell your webserver, that a file
|
* ====================
|
||||||
// for serving 403 and 404 pages is /smart_build.php. Also, you need to turn off indexes.
|
* Advanced build
|
||||||
|
* ====================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Strategies for file generation. Also known as an "advanced build". If you don't have performance
|
||||||
|
// issues, you can safely ignore that part, because it's hard to configure and won't even work on
|
||||||
|
// your free webhosting.
|
||||||
|
//
|
||||||
|
// A strategy is a function, that given the PHP environment and ($fun, $array) variable pair, returns
|
||||||
|
// an $action array or false.
|
||||||
|
//
|
||||||
|
// $fun - a controller function name, see inc/controller.php. This is named after functions, so that
|
||||||
|
// we can generate the files in daemon.
|
||||||
|
//
|
||||||
|
// $array - arguments to be passed
|
||||||
|
//
|
||||||
|
// $action - action to be taken. It's an array, and the first element of it is one of the following:
|
||||||
|
// * "immediate" - generate the page immediately
|
||||||
|
// * "defer" - defer page generation to a moment a worker daemon gets to build it (serving a stale
|
||||||
|
// page in the meantime). The remaining arguments are daemon-specific. Daemon isn't
|
||||||
|
// implemented yet :DDDD inb4 while(true) { generate(Queue::Get()) }; (which is probably it).
|
||||||
|
// * "build_on_load" - defer page generation to a moment, when the user actually accesses the page.
|
||||||
|
// This is a smart_build behaviour. You shouldn't use this one too much, if you
|
||||||
|
// use it for active boards, the server may choke due to a possible race condition.
|
||||||
|
// See my blog post: https://engine.vichan.net/blog/res/2.html
|
||||||
|
//
|
||||||
|
// So, let's assume we want to build a thread 1324 on board /b/, because a new post appeared there.
|
||||||
|
// We try the first strategy, giving it arguments: 'sb_thread', array('b', 1324). The strategy will
|
||||||
|
// now return a value $action, denoting an action to do. If $action is false, we try another strategy.
|
||||||
|
//
|
||||||
|
// As I said, configuration isn't easy.
|
||||||
|
//
|
||||||
|
// 1. chmod 0777 directories: tmp/locks/ and tmp/queue/.
|
||||||
|
// 2. serve 403 and 404 requests to go thru smart_build.php
|
||||||
|
// for nginx, this blog post contains config snippets: https://engine.vichan.net/blog/res/2.html
|
||||||
|
// 3. disable indexes in your webserver
|
||||||
|
// 4. launch any number of daemons (eg. twice your number of threads?) using the command:
|
||||||
|
// $ tools/worker.php
|
||||||
|
// You don't need to do that step if you are not going to use the "defer" option.
|
||||||
|
// 5. enable smart_build_helper (see below)
|
||||||
|
// 6. edit the strategies (see inc/functions.php for the builtin ones). You can use lambdas. I will test
|
||||||
|
// various ones and include one that works best for me.
|
||||||
|
$config['generation_strategies'] = array();
|
||||||
|
// Add a sane strategy. It forces to immediately generate a page user is about to land on. Otherwise,
|
||||||
|
// it has no opinion, so it needs a fallback strategy.
|
||||||
|
$config['generation_strategies'][] = 'strategy_sane';
|
||||||
|
// Add an immediate catch-all strategy. This is the default function of imageboards: generate all pages
|
||||||
|
// on post time.
|
||||||
|
$config['generation_strategies'][] = 'strategy_immediate';
|
||||||
|
// NOT RECOMMENDED: Instead of an all-"immediate" strategy, you can use an all-"build_on_load" one (used
|
||||||
|
// to be initialized using $config['smart_build']; ) for all pages instead of those to be build
|
||||||
|
// immediately. A rebuild done in this mode should remove all your static files
|
||||||
|
// $config['generation_strategies'][1] = 'strategy_smart_build';
|
||||||
|
|
||||||
|
// Deprecated. Leave it false. See above.
|
||||||
$config['smart_build'] = false;
|
$config['smart_build'] = false;
|
||||||
|
|
||||||
// Smart build related: when a file doesn't exist, where should we redirect?
|
// Use smart_build.php for dispatching missing requests. It may be useful without smart_build or advanced
|
||||||
|
// build, for example it will regenerate the missing files.
|
||||||
|
$config['smart_build_helper'] = true;
|
||||||
|
|
||||||
|
// smart_build.php: when a file doesn't exist, where should we redirect?
|
||||||
$config['page_404'] = '/404.html';
|
$config['page_404'] = '/404.html';
|
||||||
|
|
||||||
// Smart build related: extra entrypoints.
|
// Extra controller entrypoints. Controller is used only by smart_build and advanced build.
|
||||||
$config['smart_build_entrypoints'] = array();
|
$config['controller_entrypoints'] = array();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
|
@ -1238,6 +1352,8 @@
|
||||||
$config['mod']['link_bumpunlock'] = '[-Sage]';
|
$config['mod']['link_bumpunlock'] = '[-Sage]';
|
||||||
$config['mod']['link_editpost'] = '[Edit]';
|
$config['mod']['link_editpost'] = '[Edit]';
|
||||||
$config['mod']['link_move'] = '[Move]';
|
$config['mod']['link_move'] = '[Move]';
|
||||||
|
$config['mod']['link_cycle'] = '[Cycle]';
|
||||||
|
$config['mod']['link_uncycle'] = '[-Cycle]';
|
||||||
|
|
||||||
// Moderator capcodes.
|
// Moderator capcodes.
|
||||||
$config['capcode'] = ' <span class="capcode">## %s</span>';
|
$config['capcode'] = ' <span class="capcode">## %s</span>';
|
||||||
|
@ -1381,6 +1497,9 @@
|
||||||
$config['mod']['deletebyip_global'] = ADMIN;
|
$config['mod']['deletebyip_global'] = ADMIN;
|
||||||
// Sticky a thread
|
// Sticky a thread
|
||||||
$config['mod']['sticky'] = MOD;
|
$config['mod']['sticky'] = MOD;
|
||||||
|
// Cycle a thread
|
||||||
|
$config['mod']['cycle'] = MOD;
|
||||||
|
$config['cycle_limit'] = &$config['reply_limit'];
|
||||||
// Lock a thread
|
// Lock a thread
|
||||||
$config['mod']['lock'] = MOD;
|
$config['mod']['lock'] = MOD;
|
||||||
// Post in a locked thread
|
// Post in a locked thread
|
||||||
|
@ -1491,6 +1610,9 @@
|
||||||
$config['mod']['ban_appeals'] = MOD;
|
$config['mod']['ban_appeals'] = MOD;
|
||||||
// View the recent posts page
|
// View the recent posts page
|
||||||
$config['mod']['recent'] = MOD;
|
$config['mod']['recent'] = MOD;
|
||||||
|
// Create pages
|
||||||
|
$config['mod']['edit_pages'] = MOD;
|
||||||
|
$config['pages_max'] = 10;
|
||||||
|
|
||||||
// Config editor permissions
|
// Config editor permissions
|
||||||
$config['mod']['config'] = array();
|
$config['mod']['config'] = array();
|
||||||
|
@ -1537,14 +1659,19 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
* Public post search
|
* Public pages
|
||||||
* ====================
|
* ====================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Public post search settings
|
||||||
$config['search'] = array();
|
$config['search'] = array();
|
||||||
|
|
||||||
// Enable the search form
|
// Enable the search form
|
||||||
$config['search']['enable'] = false;
|
$config['search']['enable'] = false;
|
||||||
|
|
||||||
|
// Enable search in the board index.
|
||||||
|
$config['board_search'] = false;
|
||||||
|
|
||||||
// Maximal number of queries per IP address per minutes
|
// Maximal number of queries per IP address per minutes
|
||||||
$config['search']['queries_per_minutes'] = Array(15, 2);
|
$config['search']['queries_per_minutes'] = Array(15, 2);
|
||||||
|
|
||||||
|
@ -1557,6 +1684,9 @@
|
||||||
// Boards for searching
|
// Boards for searching
|
||||||
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
|
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
|
||||||
|
|
||||||
|
// Enable public logs? 0: NO, 1: YES, 2: YES, but drop names
|
||||||
|
$config['public_logs'] = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
* Events (PHP 5.3.0+)
|
* Events (PHP 5.3.0+)
|
||||||
|
@ -1591,6 +1721,45 @@
|
||||||
// Example: Adding the pre-markup post body to the API as "com_nomarkup".
|
// Example: Adding the pre-markup post body to the API as "com_nomarkup".
|
||||||
// $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup');
|
// $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==================
|
||||||
|
* NNTPChan settings
|
||||||
|
* ==================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Please keep in mind that NNTPChan support in vichan isn't finished yet / is in an experimental
|
||||||
|
* state. Please join #nntpchan on Rizon in order to peer with someone.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$config['nntpchan'] = array();
|
||||||
|
|
||||||
|
// Enable NNTPChan integration
|
||||||
|
$config['nntpchan']['enabled'] = false;
|
||||||
|
|
||||||
|
// NNTP server
|
||||||
|
$config['nntpchan']['server'] = "localhost:1119";
|
||||||
|
|
||||||
|
// Global dispatch array. Add your boards to it to enable them. Please make
|
||||||
|
// sure that this setting is set in a global context.
|
||||||
|
$config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test'
|
||||||
|
|
||||||
|
// Trusted peer - an IP address of your NNTPChan instance. This peer will have
|
||||||
|
// increased capabilities, eg.: will evade spamfilter.
|
||||||
|
$config['nntpchan']['trusted_peer'] = '127.0.0.1';
|
||||||
|
|
||||||
|
// Salt for message ID generation. Keep it long and secure.
|
||||||
|
$config['nntpchan']['salt'] = 'change_me+please';
|
||||||
|
|
||||||
|
// A local message ID domain. Make sure to change it.
|
||||||
|
$config['nntpchan']['domain'] = 'example.vichan.net';
|
||||||
|
|
||||||
|
// An NNTPChan group name.
|
||||||
|
// Please set this setting in your board/config.php, not globally.
|
||||||
|
$config['nntpchan']['group'] = false; // eg. 'overchan.test'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ====================
|
* ====================
|
||||||
* Other/uncategorized
|
* Other/uncategorized
|
||||||
|
@ -1687,3 +1856,9 @@
|
||||||
// If set to 0, it won't upgrade hashes using old password encryption schema, only create new.
|
// If set to 0, it won't upgrade hashes using old password encryption schema, only create new.
|
||||||
// You can set it to a higher value, to further migrate to other password hashing function.
|
// You can set it to a higher value, to further migrate to other password hashing function.
|
||||||
$config['password_crypt_version'] = 1;
|
$config['password_crypt_version'] = 1;
|
||||||
|
|
||||||
|
// Use CAPTCHA for reports?
|
||||||
|
$config['report_captcha'] = false;
|
||||||
|
|
||||||
|
// Allowed HTML tags in ?/edit_pages.
|
||||||
|
$config['allowed_html'] = 'a[href|title],p,br,li,ol,ul,strong,em,u,h2,b,i,tt,div,img[src|alt|title],hr';
|
||||||
|
|
108
inc/controller.php
Normal file
108
inc/controller.php
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// This file contains the controller part of vichan
|
||||||
|
|
||||||
|
// don't bother with that unless you use smart build or advanced build
|
||||||
|
// you can use those parts for your own implementations though :^)
|
||||||
|
|
||||||
|
defined('TINYBOARD') or exit;
|
||||||
|
|
||||||
|
function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page;
|
||||||
|
if ($page < 1) return false;
|
||||||
|
if (!openBoard($b)) return false;
|
||||||
|
if ($page > $config['max_pages']) return false;
|
||||||
|
$config['try_smarter'] = true;
|
||||||
|
$build_pages = array($page);
|
||||||
|
buildIndex("skip");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_api_board($b, $page = 0) { $page = (int)$page;
|
||||||
|
return sb_board($b, $page + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread;
|
||||||
|
if ($thread < 1) return false;
|
||||||
|
|
||||||
|
if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false;
|
||||||
|
|
||||||
|
if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false;
|
||||||
|
|
||||||
|
$query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b));
|
||||||
|
if (!$query->execute()) return false;
|
||||||
|
|
||||||
|
$s = $query->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$max = $s['max'];
|
||||||
|
|
||||||
|
if ($thread > $max) return false;
|
||||||
|
|
||||||
|
$query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b));
|
||||||
|
$query->bindValue(':id', $thread);
|
||||||
|
|
||||||
|
if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) {
|
||||||
|
Cache::set("thread_exists_".$b."_".$thread, "no", 3600);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($slugcheck && $config['slugify']) {
|
||||||
|
global $request;
|
||||||
|
|
||||||
|
$link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b));
|
||||||
|
$link = "/".$b."/".$config['dir']['res'].$link;
|
||||||
|
|
||||||
|
if ($link != $request) {
|
||||||
|
header("Location: $link", true, 301);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway
|
||||||
|
global $request;
|
||||||
|
$r = str_replace("+50", "", $request);
|
||||||
|
$r = substr($r, 1); // Cut the slash
|
||||||
|
|
||||||
|
if (file_exists($r)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!openBoard($b)) return false;
|
||||||
|
buildThread($thread);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_thread_slugcheck($b, $thread) {
|
||||||
|
return sb_thread($b, $thread, true);
|
||||||
|
}
|
||||||
|
function sb_thread_slugcheck50($b, $thread) {
|
||||||
|
return sb_thread($b, $thread, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_api($b) { global $config, $build_pages;
|
||||||
|
if (!openBoard($b)) return false;
|
||||||
|
$config['try_smarter'] = true;
|
||||||
|
$build_pages = array(-1);
|
||||||
|
buildIndex();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_ukko() {
|
||||||
|
rebuildTheme("ukko", "post-thread");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_catalog($b) {
|
||||||
|
if (!openBoard($b)) return false;
|
||||||
|
|
||||||
|
rebuildTheme("catalog", "post-thread", $b);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_recent() {
|
||||||
|
rebuildTheme("recent", "post-thread");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sb_sitemap() {
|
||||||
|
rebuildTheme("sitemap", "all");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -94,20 +94,15 @@ function error($message, $priority = true, $debug_stuff = false) {
|
||||||
$debug_stuff['backtrace'] = debug_backtrace();
|
$debug_stuff['backtrace'] = debug_backtrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the bad request header, necessary for AJAX posts
|
|
||||||
// czaks: is it really so? the ajax errors only work when this is commented out
|
|
||||||
// better yet use it when ajax is disabled
|
|
||||||
if (!isset ($_POST['json_response'])) {
|
|
||||||
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is there a reason to disable this?
|
|
||||||
if (isset($_POST['json_response'])) {
|
if (isset($_POST['json_response'])) {
|
||||||
header('Content-Type: text/json; charset=utf-8');
|
header('Content-Type: text/json; charset=utf-8');
|
||||||
die(json_encode(array(
|
die(json_encode(array(
|
||||||
'error' => $message
|
'error' => $message
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
|
||||||
|
}
|
||||||
|
|
||||||
$pw = $config['db']['password'];
|
$pw = $config['db']['password'];
|
||||||
$debug_callback = function(&$item) use (&$debug_callback, $pw) {
|
$debug_callback = function(&$item) use (&$debug_callback, $pw) {
|
||||||
|
|
|
@ -18,6 +18,12 @@ require_once 'inc/template.php';
|
||||||
require_once 'inc/database.php';
|
require_once 'inc/database.php';
|
||||||
require_once 'inc/events.php';
|
require_once 'inc/events.php';
|
||||||
require_once 'inc/api.php';
|
require_once 'inc/api.php';
|
||||||
|
require_once 'inc/mod/auth.php';
|
||||||
|
require_once 'inc/lock.php';
|
||||||
|
require_once 'inc/queue.php';
|
||||||
|
require_once 'inc/polyfill.php';
|
||||||
|
@include_once 'inc/lib/parsedown/Parsedown.php'; // fail silently, this isn't a critical piece of code
|
||||||
|
|
||||||
if (!extension_loaded('gettext')) {
|
if (!extension_loaded('gettext')) {
|
||||||
require_once 'inc/lib/gettext/gettext.inc';
|
require_once 'inc/lib/gettext/gettext.inc';
|
||||||
}
|
}
|
||||||
|
@ -89,6 +95,8 @@ function loadConfig() {
|
||||||
'db',
|
'db',
|
||||||
'api',
|
'api',
|
||||||
'cache',
|
'cache',
|
||||||
|
'lock',
|
||||||
|
'queue',
|
||||||
'cookies',
|
'cookies',
|
||||||
'error',
|
'error',
|
||||||
'dir',
|
'dir',
|
||||||
|
@ -125,7 +133,7 @@ function loadConfig() {
|
||||||
// So, we may store the locale in a tmp/ filesystem.
|
// So, we may store the locale in a tmp/ filesystem.
|
||||||
|
|
||||||
if (file_exists($fn = 'tmp/cache/locale_' . $boardsuffix ) ) {
|
if (file_exists($fn = 'tmp/cache/locale_' . $boardsuffix ) ) {
|
||||||
$config['locale'] = file_get_contents($fn);
|
$config['locale'] = @file_get_contents($fn);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$config['locale'] = 'en';
|
$config['locale'] = 'en';
|
||||||
|
@ -136,13 +144,13 @@ function loadConfig() {
|
||||||
$configstr .= file_get_contents($board['dir'] . '/config.php');
|
$configstr .= file_get_contents($board['dir'] . '/config.php');
|
||||||
}
|
}
|
||||||
$matches = array();
|
$matches = array();
|
||||||
preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
|
preg_match_all('/[^\/#*]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
|
||||||
if ($matches && isset ($matches[2]) && $matches[2]) {
|
if ($matches && isset ($matches[2]) && $matches[2]) {
|
||||||
$matches = $matches[2];
|
$matches = $matches[2];
|
||||||
$config['locale'] = $matches[count($matches)-1];
|
$config['locale'] = $matches[count($matches)-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($fn, $config['locale']);
|
@file_put_contents($fn, $config['locale']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config['locale'] != $current_locale) {
|
if ($config['locale'] != $current_locale) {
|
||||||
|
@ -281,9 +289,6 @@ function loadConfig() {
|
||||||
if ($config['syslog'])
|
if ($config['syslog'])
|
||||||
openlog('tinyboard', LOG_ODELAY, LOG_SYSLOG); // open a connection to sysem logger
|
openlog('tinyboard', LOG_ODELAY, LOG_SYSLOG); // open a connection to sysem logger
|
||||||
|
|
||||||
if ($config['recaptcha'])
|
|
||||||
require_once 'inc/lib/recaptcha/recaptchalib.php';
|
|
||||||
|
|
||||||
if ($config['cache']['enabled'])
|
if ($config['cache']['enabled'])
|
||||||
require_once 'inc/cache.php';
|
require_once 'inc/cache.php';
|
||||||
|
|
||||||
|
@ -522,7 +527,8 @@ function setupBoard($array) {
|
||||||
$board = array(
|
$board = array(
|
||||||
'uri' => $array['uri'],
|
'uri' => $array['uri'],
|
||||||
'title' => $array['title'],
|
'title' => $array['title'],
|
||||||
'subtitle' => $array['subtitle']
|
'subtitle' => $array['subtitle'],
|
||||||
|
#'indexed' => $array['indexed'],
|
||||||
);
|
);
|
||||||
|
|
||||||
// older versions
|
// older versions
|
||||||
|
@ -547,14 +553,19 @@ function setupBoard($array) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function openBoard($uri) {
|
function openBoard($uri) {
|
||||||
global $config, $build_pages;
|
global $config, $build_pages, $board;
|
||||||
|
|
||||||
if ($config['try_smarter'])
|
if ($config['try_smarter'])
|
||||||
$build_pages = array();
|
$build_pages = array();
|
||||||
|
|
||||||
$board = getBoardInfo($uri);
|
// And what if we don't really need to change a board we have opened?
|
||||||
if ($board) {
|
if (isset ($board) && isset ($board['uri']) && $board['uri'] == $uri) {
|
||||||
setupBoard($board);
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$b = getBoardInfo($uri);
|
||||||
|
if ($b) {
|
||||||
|
setupBoard($b);
|
||||||
|
|
||||||
if (function_exists('after_open_board')) {
|
if (function_exists('after_open_board')) {
|
||||||
after_open_board();
|
after_open_board();
|
||||||
|
@ -878,7 +889,7 @@ function displayBan($ban) {
|
||||||
Element('page.html', array(
|
Element('page.html', array(
|
||||||
'title' => _('Banned!'),
|
'title' => _('Banned!'),
|
||||||
'config' => $config,
|
'config' => $config,
|
||||||
'nojavascript' => true,
|
'boardlist' => createBoardlist(isset($mod) ? $mod : false),
|
||||||
'body' => Element('banned.html', array(
|
'body' => Element('banned.html', array(
|
||||||
'config' => $config,
|
'config' => $config,
|
||||||
'ban' => $ban,
|
'ban' => $ban,
|
||||||
|
@ -1016,7 +1027,7 @@ function insertFloodPost(array $post) {
|
||||||
|
|
||||||
function post(array $post) {
|
function post(array $post) {
|
||||||
global $pdo, $board;
|
global $pdo, $board;
|
||||||
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, 0, :embed, :slug)", $board['uri']));
|
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, :cycle, 0, :embed, :slug)", $board['uri']));
|
||||||
|
|
||||||
// Basic stuff
|
// Basic stuff
|
||||||
if (!empty($post['subject'])) {
|
if (!empty($post['subject'])) {
|
||||||
|
@ -1056,6 +1067,12 @@ function post(array $post) {
|
||||||
$query->bindValue(':locked', false, PDO::PARAM_INT);
|
$query->bindValue(':locked', false, PDO::PARAM_INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($post['op'] && $post['mod'] && isset($post['cycle']) && $post['cycle']) {
|
||||||
|
$query->bindValue(':cycle', true, PDO::PARAM_INT);
|
||||||
|
} else {
|
||||||
|
$query->bindValue(':cycle', false, PDO::PARAM_INT);
|
||||||
|
}
|
||||||
|
|
||||||
if ($post['mod'] && isset($post['capcode']) && $post['capcode']) {
|
if ($post['mod'] && isset($post['capcode']) && $post['capcode']) {
|
||||||
$query->bindValue(':capcode', $post['capcode'], PDO::PARAM_INT);
|
$query->bindValue(':capcode', $post['capcode'], PDO::PARAM_INT);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1128,6 +1145,8 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
|
||||||
$files = json_decode($post['files']);
|
$files = json_decode($post['files']);
|
||||||
$file_to_delete = $file !== false ? $files[(int)$file] : (object)array('file' => false);
|
$file_to_delete = $file !== false ? $files[(int)$file] : (object)array('file' => false);
|
||||||
|
|
||||||
|
if (!$files[0]) error(_('That post has no files.'));
|
||||||
|
|
||||||
if ($files[0]->file == 'deleted' && $post['num_files'] == 1 && !$post['thread'])
|
if ($files[0]->file == 'deleted' && $post['num_files'] == 1 && !$post['thread'])
|
||||||
return; // Can't delete OP's image completely.
|
return; // Can't delete OP's image completely.
|
||||||
|
|
||||||
|
@ -1139,8 +1158,10 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
|
||||||
foreach ($files as $i => $f) {
|
foreach ($files as $i => $f) {
|
||||||
if (($file !== false && $i == $file) || $file === null) {
|
if (($file !== false && $i == $file) || $file === null) {
|
||||||
// Delete thumbnail
|
// Delete thumbnail
|
||||||
|
if (isset ($f->thumb) && $f->thumb) {
|
||||||
file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb);
|
file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb);
|
||||||
unset($files[$i]->thumb);
|
unset($files[$i]->thumb);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete file
|
// Delete file
|
||||||
file_unlink($board['dir'] . $config['dir']['img'] . $f->file);
|
file_unlink($board['dir'] . $config['dir']['img'] . $f->file);
|
||||||
|
@ -1162,19 +1183,22 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
|
||||||
|
|
||||||
// rebuild post (markup)
|
// rebuild post (markup)
|
||||||
function rebuildPost($id) {
|
function rebuildPost($id) {
|
||||||
global $board;
|
global $board, $mod;
|
||||||
|
|
||||||
$query = prepare(sprintf("SELECT `body_nomarkup`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
||||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
if ((!$post = $query->fetch(PDO::FETCH_ASSOC)) || !$post['body_nomarkup'])
|
if ((!$post = $query->fetch(PDO::FETCH_ASSOC)) || !$post['body_nomarkup'])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
markup($body = &$post['body_nomarkup']);
|
markup($post['body'] = &$post['body_nomarkup']);
|
||||||
|
$post = (object)$post;
|
||||||
|
event('rebuildpost', $post);
|
||||||
|
$post = (array)$post;
|
||||||
|
|
||||||
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `body` = :body WHERE `id` = :id", $board['uri']));
|
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `body` = :body WHERE `id` = :id", $board['uri']));
|
||||||
$query->bindValue(':body', $body);
|
$query->bindValue(':body', $post['body']);
|
||||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
@ -1263,7 +1287,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clean() {
|
function clean($pid = false) {
|
||||||
global $board, $config;
|
global $board, $config;
|
||||||
$offset = round($config['max_pages']*$config['threads_per_page']);
|
$offset = round($config['max_pages']*$config['threads_per_page']);
|
||||||
|
|
||||||
|
@ -1274,6 +1298,39 @@ function clean() {
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
|
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||||
deletePost($post['id'], false, false);
|
deletePost($post['id'], false, false);
|
||||||
|
if ($pid) modLog("Automatically deleting thread #{$post['id']} due to new thread #{$pid}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump off threads with X replies earlier, spam prevention method
|
||||||
|
if ($config['early_404']) {
|
||||||
|
$offset = round($config['early_404_page']*$config['threads_per_page']);
|
||||||
|
$query = prepare(sprintf("SELECT `id` AS `thread_id`, (SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `thread` = `thread_id`) AS `reply_count` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001", $board['uri'], $board['uri']));
|
||||||
|
$query->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
if ($config['early_404_staged']) {
|
||||||
|
$page = $config['early_404_page'];
|
||||||
|
$iter = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$page = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
if ($post['reply_count'] < $page*$config['early_404_replies']) {
|
||||||
|
deletePost($post['thread_id'], false, false);
|
||||||
|
if ($pid) modLog("Automatically deleting thread #{$post['thread_id']} due to new thread #{$pid} (early 404 is set, #{$post['thread_id']} had {$post['reply_count']} replies)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config['early_404_staged']) {
|
||||||
|
$iter++;
|
||||||
|
|
||||||
|
if ($iter == $config['threads_per_page']) {
|
||||||
|
$page++;
|
||||||
|
$iter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,7 +1344,8 @@ function thread_find_page($thread) {
|
||||||
return floor(($config['threads_per_page'] + $index) / $config['threads_per_page']);
|
return floor(($config['threads_per_page'] + $index) / $config['threads_per_page']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function index($page, $mod=false) {
|
// $brief means that we won't need to generate anything yet
|
||||||
|
function index($page, $mod=false, $brief = false) {
|
||||||
global $board, $config, $debug;
|
global $board, $config, $debug;
|
||||||
|
|
||||||
$body = '';
|
$body = '';
|
||||||
|
@ -1318,6 +1376,7 @@ function index($page, $mod=false) {
|
||||||
unset($cached);
|
unset($cached);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($cached)) {
|
if (!isset($cached)) {
|
||||||
$posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri']));
|
$posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri']));
|
||||||
$posts->bindValue(':id', $th['id']);
|
$posts->bindValue(':id', $th['id']);
|
||||||
|
@ -1357,8 +1416,11 @@ function index($page, $mod=false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$threads[] = $thread;
|
$threads[] = $thread;
|
||||||
|
|
||||||
|
if (!$brief) {
|
||||||
$body .= $thread->build(true);
|
$body .= $thread->build(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($config['file_board']) {
|
if ($config['file_board']) {
|
||||||
$body = Element('fileboard.html', array('body' => $body, 'mod' => $mod));
|
$body = Element('fileboard.html', array('body' => $body, 'mod' => $mod));
|
||||||
|
@ -1578,27 +1640,28 @@ function checkMute() {
|
||||||
function buildIndex($global_api = "yes") {
|
function buildIndex($global_api = "yes") {
|
||||||
global $board, $config, $build_pages;
|
global $board, $config, $build_pages;
|
||||||
|
|
||||||
if (!$config['smart_build']) {
|
$catalog_api_action = generation_strategy('sb_api', array($board['uri']));
|
||||||
$pages = getPages();
|
|
||||||
if (!$config['try_smarter'])
|
$pages = null;
|
||||||
$antibot = create_antibot($board['uri']);
|
$antibot = null;
|
||||||
|
|
||||||
if ($config['api']['enabled']) {
|
if ($config['api']['enabled']) {
|
||||||
$api = new Api();
|
$api = new Api();
|
||||||
$catalog = array();
|
$catalog = array();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for ($page = 1; $page <= $config['max_pages']; $page++) {
|
for ($page = 1; $page <= $config['max_pages']; $page++) {
|
||||||
$filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page));
|
$filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page));
|
||||||
$jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0
|
$jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0
|
||||||
|
|
||||||
if ((!$config['api']['enabled'] || $global_api == "skip" || $config['smart_build']) && $config['try_smarter']
|
$wont_build_this_page = $config['try_smarter'] && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages);
|
||||||
&& isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) )
|
|
||||||
|
if ((!$config['api']['enabled'] || $global_api == "skip") && $wont_build_this_page)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!$config['smart_build']) {
|
$action = generation_strategy('sb_board', array($board['uri'], $page));
|
||||||
$content = index($page);
|
if ($action == 'rebuild' || $catalog_api_action == 'rebuild') {
|
||||||
|
$content = index($page, false, $wont_build_this_page);
|
||||||
if (!$content)
|
if (!$content)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1609,17 +1672,21 @@ function buildIndex($global_api = "yes") {
|
||||||
file_write($jsonFilename, $json);
|
file_write($jsonFilename, $json);
|
||||||
|
|
||||||
$catalog[$page-1] = $threads;
|
$catalog[$page-1] = $threads;
|
||||||
}
|
|
||||||
|
|
||||||
if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages)
|
if ($wont_build_this_page) continue;
|
||||||
&& !empty($build_pages) && !in_array($page, $build_pages) )
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($config['try_smarter']) {
|
if ($config['try_smarter']) {
|
||||||
$antibot = create_antibot($board['uri'], 0 - $page);
|
$antibot = create_antibot($board['uri'], 0 - $page);
|
||||||
$content['current_page'] = $page;
|
$content['current_page'] = $page;
|
||||||
}
|
}
|
||||||
|
elseif (!$antibot) {
|
||||||
|
$antibot = create_antibot($board['uri']);
|
||||||
|
}
|
||||||
$antibot->reset();
|
$antibot->reset();
|
||||||
|
if (!$pages) {
|
||||||
|
$pages = getPages();
|
||||||
|
}
|
||||||
$content['pages'] = $pages;
|
$content['pages'] = $pages;
|
||||||
$content['pages'][$page-1]['selected'] = true;
|
$content['pages'][$page-1]['selected'] = true;
|
||||||
$content['btn'] = getPageButtons($content['pages']);
|
$content['btn'] = getPageButtons($content['pages']);
|
||||||
|
@ -1627,13 +1694,14 @@ function buildIndex($global_api = "yes") {
|
||||||
|
|
||||||
file_write($filename, Element('index.html', $content));
|
file_write($filename, Element('index.html', $content));
|
||||||
}
|
}
|
||||||
else {
|
elseif ($action == 'delete' || $catalog_api_action == 'delete') {
|
||||||
file_unlink($filename);
|
file_unlink($filename);
|
||||||
file_unlink($jsonFilename);
|
file_unlink($jsonFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$config['smart_build'] && $page < $config['max_pages']) {
|
// $action is an action for our last page
|
||||||
|
if (($catalog_api_action == 'rebuild' || $action == 'rebuild' || $action == 'delete') && $page < $config['max_pages']) {
|
||||||
for (;$page<=$config['max_pages'];$page++) {
|
for (;$page<=$config['max_pages'];$page++) {
|
||||||
$filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page));
|
$filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page));
|
||||||
file_unlink($filename);
|
file_unlink($filename);
|
||||||
|
@ -1647,13 +1715,13 @@ function buildIndex($global_api = "yes") {
|
||||||
|
|
||||||
// json api catalog
|
// json api catalog
|
||||||
if ($config['api']['enabled'] && $global_api != "skip") {
|
if ($config['api']['enabled'] && $global_api != "skip") {
|
||||||
if ($config['smart_build']) {
|
if ($catalog_api_action == 'delete') {
|
||||||
$jsonFilename = $board['dir'] . 'catalog.json';
|
$jsonFilename = $board['dir'] . 'catalog.json';
|
||||||
file_unlink($jsonFilename);
|
file_unlink($jsonFilename);
|
||||||
$jsonFilename = $board['dir'] . 'threads.json';
|
$jsonFilename = $board['dir'] . 'threads.json';
|
||||||
file_unlink($jsonFilename);
|
file_unlink($jsonFilename);
|
||||||
}
|
}
|
||||||
else {
|
elseif ($catalog_api_action == 'rebuild') {
|
||||||
$json = json_encode($api->translateCatalog($catalog));
|
$json = json_encode($api->translateCatalog($catalog));
|
||||||
$jsonFilename = $board['dir'] . 'catalog.json';
|
$jsonFilename = $board['dir'] . 'catalog.json';
|
||||||
file_write($jsonFilename, $json);
|
file_write($jsonFilename, $json);
|
||||||
|
@ -1706,13 +1774,15 @@ function buildJavascript() {
|
||||||
function checkDNSBL() {
|
function checkDNSBL() {
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
|
|
||||||
if (isIPv6())
|
if (isIPv6())
|
||||||
return; // No IPv6 support yet.
|
return; // No IPv6 support yet.
|
||||||
|
|
||||||
if (!isset($_SERVER['REMOTE_ADDR']))
|
if (!isset($_SERVER['REMOTE_ADDR']))
|
||||||
return; // Fix your web server configuration
|
return; // Fix your web server configuration
|
||||||
|
|
||||||
|
if (preg_match("/^(::(ffff:)?)?(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|0\.|255\.)/", $_SERVER['REMOTE_ADDR']))
|
||||||
|
return; // It's pointless to check for local IP addresses in dnsbls, isn't it?
|
||||||
|
|
||||||
if (in_array($_SERVER['REMOTE_ADDR'], $config['dnsbl_exceptions']))
|
if (in_array($_SERVER['REMOTE_ADDR'], $config['dnsbl_exceptions']))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1845,7 +1915,11 @@ function extract_modifiers($body) {
|
||||||
return $modifiers;
|
return $modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
function markup(&$body, $track_cites = false) {
|
function remove_modifiers($body) {
|
||||||
|
return preg_replace('@<tinyboard ([\w\s]+)>(.+?)</tinyboard>@usm', '', $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
function markup(&$body, $track_cites = false, $op = false) {
|
||||||
global $board, $config, $markup_urls;
|
global $board, $config, $markup_urls;
|
||||||
|
|
||||||
$modifiers = extract_modifiers($body);
|
$modifiers = extract_modifiers($body);
|
||||||
|
@ -1941,7 +2015,7 @@ function markup(&$body, $track_cites = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($cited_posts[$cite])) {
|
if (isset($cited_posts[$cite])) {
|
||||||
$replacement = '<a onclick="highlightReply(\''.$cite.'\');" href="' .
|
$replacement = '<a onclick="highlightReply(\''.$cite.'\', event);" href="' .
|
||||||
$config['root'] . $board['dir'] . $config['dir']['res'] .
|
$config['root'] . $board['dir'] . $config['dir']['res'] .
|
||||||
link_for(array('id' => $cite, 'thread' => $cited_posts[$cite])) . '#' . $cite . '">' .
|
link_for(array('id' => $cite, 'thread' => $cited_posts[$cite])) . '#' . $cite . '">' .
|
||||||
'>>' . $cite .
|
'>>' . $cite .
|
||||||
|
@ -2040,7 +2114,7 @@ function markup(&$body, $track_cites = false) {
|
||||||
|
|
||||||
$replacement = '<a ' .
|
$replacement = '<a ' .
|
||||||
($_board == $board['uri'] ?
|
($_board == $board['uri'] ?
|
||||||
'onclick="highlightReply(\''.$cite.'\');" '
|
'onclick="highlightReply(\''.$cite.'\', event);" '
|
||||||
: '') . 'href="' . $link . '">' .
|
: '') . 'href="' . $link . '">' .
|
||||||
'>>>/' . $_board . '/' . $cite .
|
'>>>/' . $_board . '/' . $cite .
|
||||||
'</a>';
|
'</a>';
|
||||||
|
@ -2144,16 +2218,7 @@ function strip_combining_chars($str) {
|
||||||
$o = 0;
|
$o = 0;
|
||||||
$ord = ordutf8($char, $o);
|
$ord = ordutf8($char, $o);
|
||||||
|
|
||||||
if ($ord >= 768 && $ord <= 879)
|
if ( ($ord >= 768 && $ord <= 879) || ($ord >= 1536 && $ord <= 1791) || ($ord >= 3655 && $ord <= 3659) || ($ord >= 7616 && $ord <= 7679) || ($ord >= 8400 && $ord <= 8447) || ($ord >= 65056 && $ord <= 65071))
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($ord >= 7616 && $ord <= 7679)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($ord >= 8400 && $ord <= 8447)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($ord >= 65056 && $ord <= 65071)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$str .= $char;
|
$str .= $char;
|
||||||
|
@ -2177,7 +2242,9 @@ function buildThread($id, $return = false, $mod = false) {
|
||||||
if ($config['try_smarter'] && !$mod)
|
if ($config['try_smarter'] && !$mod)
|
||||||
$build_pages[] = thread_find_page($id);
|
$build_pages[] = thread_find_page($id);
|
||||||
|
|
||||||
if (!$config['smart_build'] || $return || $mod) {
|
$action = generation_strategy('sb_thread', array($board['uri'], $id));
|
||||||
|
|
||||||
|
if ($action == 'rebuild' || $return || $mod) {
|
||||||
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri']));
|
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri']));
|
||||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
@ -2212,26 +2279,26 @@ function buildThread($id, $return = false, $mod = false) {
|
||||||
));
|
));
|
||||||
|
|
||||||
// json api
|
// json api
|
||||||
if ($config['api']['enabled']) {
|
if ($config['api']['enabled'] && !$mod) {
|
||||||
$api = new Api();
|
$api = new Api();
|
||||||
$json = json_encode($api->translateThread($thread));
|
$json = json_encode($api->translateThread($thread));
|
||||||
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
|
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
|
||||||
file_write($jsonFilename, $json);
|
file_write($jsonFilename, $json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
elseif($action == 'delete') {
|
||||||
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
|
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
|
||||||
file_unlink($jsonFilename);
|
file_unlink($jsonFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config['smart_build'] && !$return && !$mod) {
|
if ($action == 'delete' && !$return && !$mod) {
|
||||||
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true);
|
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true);
|
||||||
file_unlink($noko50fn);
|
file_unlink($noko50fn);
|
||||||
|
|
||||||
file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id)));
|
file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id)));
|
||||||
} else if ($return) {
|
} elseif ($return) {
|
||||||
return $body;
|
return $body;
|
||||||
} else {
|
} elseif ($action == 'rebuild') {
|
||||||
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for($thread, true);
|
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for($thread, true);
|
||||||
if ($hasnoko50 || file_exists($noko50fn)) {
|
if ($hasnoko50 || file_exists($noko50fn)) {
|
||||||
buildThread50($id, $return, $mod, $thread, $antibot);
|
buildThread50($id, $return, $mod, $thread, $antibot);
|
||||||
|
@ -2372,7 +2439,7 @@ function generate_tripcode($name) {
|
||||||
if (isset($config['custom_tripcode']["##{$trip}"]))
|
if (isset($config['custom_tripcode']["##{$trip}"]))
|
||||||
$trip = $config['custom_tripcode']["##{$trip}"];
|
$trip = $config['custom_tripcode']["##{$trip}"];
|
||||||
else
|
else
|
||||||
$trip = '!!' . substr(crypt($trip, '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4)), -10);
|
$trip = '!!' . substr(crypt($trip, str_replace('+', '.', '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4))), -10);
|
||||||
} else {
|
} else {
|
||||||
if (isset($config['custom_tripcode']["#{$trip}"]))
|
if (isset($config['custom_tripcode']["#{$trip}"]))
|
||||||
$trip = $config['custom_tripcode']["#{$trip}"];
|
$trip = $config['custom_tripcode']["#{$trip}"];
|
||||||
|
@ -2461,7 +2528,7 @@ function rDNS($ip_addr) {
|
||||||
if (!$config['dns_system']) {
|
if (!$config['dns_system']) {
|
||||||
$host = gethostbyaddr($ip_addr);
|
$host = gethostbyaddr($ip_addr);
|
||||||
} else {
|
} else {
|
||||||
$resp = shell_exec_error('host -W 1 ' . $ip_addr);
|
$resp = shell_exec_error('host -W 3 ' . $ip_addr);
|
||||||
if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m))
|
if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m))
|
||||||
$host = $m[1];
|
$host = $m[1];
|
||||||
else
|
else
|
||||||
|
@ -2597,7 +2664,7 @@ function slugify($post) {
|
||||||
elseif (isset ($post['body_nomarkup']) && $post['body_nomarkup'])
|
elseif (isset ($post['body_nomarkup']) && $post['body_nomarkup'])
|
||||||
$slug = $post['body_nomarkup'];
|
$slug = $post['body_nomarkup'];
|
||||||
elseif (isset ($post['body']) && $post['body'])
|
elseif (isset ($post['body']) && $post['body'])
|
||||||
$slug = strip_html($post['body']);
|
$slug = strip_tags($post['body']);
|
||||||
|
|
||||||
// Fix UTF-8 first
|
// Fix UTF-8 first
|
||||||
$slug = mb_convert_encoding($slug, "UTF-8", "UTF-8");
|
$slug = mb_convert_encoding($slug, "UTF-8", "UTF-8");
|
||||||
|
@ -2672,3 +2739,103 @@ function link_for($post, $page50 = false, $foreignlink = false, $thread = false)
|
||||||
|
|
||||||
return sprintf($tpl, $id, $slug);
|
return sprintf($tpl, $id, $slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prettify_textarea($s){
|
||||||
|
return str_replace("\t", '	', str_replace("\n", ' ', htmlentities($s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter {
|
||||||
|
public $name = 'NoExternalImages';
|
||||||
|
public function filter(&$uri, $c, $context) {
|
||||||
|
global $config;
|
||||||
|
$ct = $context->get('CurrentToken');
|
||||||
|
|
||||||
|
if (!$ct || $ct->name !== 'img') return true;
|
||||||
|
|
||||||
|
if (!isset($uri->host) && !isset($uri->scheme)) return true;
|
||||||
|
|
||||||
|
if (!in_array($uri->scheme . '://' . $uri->host . '/', $config['allowed_offsite_urls'])) {
|
||||||
|
error('No off-site links in board announcement images.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
function purify_html($s) {
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$c = HTMLPurifier_Config::createDefault();
|
||||||
|
$c->set('HTML.Allowed', $config['allowed_html']);
|
||||||
|
$uri = $c->getDefinition('URI');
|
||||||
|
$uri->addFilter(new HTMLPurifier_URIFilter_NoExternalImages(), $c);
|
||||||
|
$purifier = new HTMLPurifier($c);
|
||||||
|
$clean_html = $purifier->purify($s);
|
||||||
|
return $clean_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function markdown($s) {
|
||||||
|
$pd = new Parsedown();
|
||||||
|
$pd->setMarkupEscaped(true);
|
||||||
|
$pd->setimagesEnabled(false);
|
||||||
|
|
||||||
|
return $pd->text($s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generation_strategy($fun, $array=array()) { global $config;
|
||||||
|
$action = false;
|
||||||
|
|
||||||
|
foreach ($config['generation_strategies'] as $s) {
|
||||||
|
if ($action = $s($fun, $array)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($action[0]) {
|
||||||
|
case 'immediate':
|
||||||
|
return 'rebuild';
|
||||||
|
case 'defer':
|
||||||
|
// Ok, it gets interesting here :)
|
||||||
|
get_queue('generate')->push(serialize(array('build', $fun, $array, $action)));
|
||||||
|
return 'ignore';
|
||||||
|
case 'build_on_load':
|
||||||
|
return 'delete';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function strategy_immediate($fun, $array) {
|
||||||
|
return array('immediate');
|
||||||
|
}
|
||||||
|
|
||||||
|
function strategy_smart_build($fun, $array) {
|
||||||
|
return array('build_on_load');
|
||||||
|
}
|
||||||
|
|
||||||
|
function strategy_sane($fun, $array) { global $config;
|
||||||
|
if (php_sapi_name() == 'cli') return false;
|
||||||
|
else if (isset($_POST['mod'])) return false;
|
||||||
|
// Thread needs to be done instantly. Same with a board page, but only if posting a new thread.
|
||||||
|
else if ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1 && isset ($_POST['page']))) return array('immediate');
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// My first, test strategy.
|
||||||
|
function strategy_first($fun, $array) {
|
||||||
|
switch ($fun) {
|
||||||
|
case 'sb_thread':
|
||||||
|
return array('defer');
|
||||||
|
case 'sb_board':
|
||||||
|
if ($array[1] > 8) return array('build_on_load');
|
||||||
|
else return array('defer');
|
||||||
|
case 'sb_api':
|
||||||
|
return array('defer');
|
||||||
|
case 'sb_catalog':
|
||||||
|
return array('defer');
|
||||||
|
case 'sb_recent':
|
||||||
|
return array('build_on_load');
|
||||||
|
case 'sb_sitemap':
|
||||||
|
return array('build_on_load');
|
||||||
|
case 'sb_ukko':
|
||||||
|
return array('defer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
|
||||||
new Twig_SimpleFilter('extension', 'twig_extension_filter'),
|
new Twig_SimpleFilter('extension', 'twig_extension_filter'),
|
||||||
new Twig_SimpleFilter('sprintf', 'sprintf'),
|
new Twig_SimpleFilter('sprintf', 'sprintf'),
|
||||||
new Twig_SimpleFilter('capcode', 'capcode'),
|
new Twig_SimpleFilter('capcode', 'capcode'),
|
||||||
|
new Twig_SimpleFilter('remove_modifiers', 'remove_modifiers'),
|
||||||
new Twig_SimpleFilter('hasPermission', 'twig_hasPermission_filter'),
|
new Twig_SimpleFilter('hasPermission', 'twig_hasPermission_filter'),
|
||||||
new Twig_SimpleFilter('date', 'twig_date_filter'),
|
new Twig_SimpleFilter('date', 'twig_date_filter'),
|
||||||
new Twig_SimpleFilter('poster_id', 'poster_id'),
|
new Twig_SimpleFilter('poster_id', 'poster_id'),
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
|
|
||||||
AUTHORS:
|
|
||||||
Mike Crawford
|
|
||||||
Ben Maurer
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
|
@ -1,277 +0,0 @@
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
* This is a PHP library that handles calling reCAPTCHA.
|
|
||||||
* - Documentation and latest version
|
|
||||||
* http://recaptcha.net/plugins/php/
|
|
||||||
* - Get a reCAPTCHA API Key
|
|
||||||
* https://www.google.com/recaptcha/admin/create
|
|
||||||
* - Discussion group
|
|
||||||
* http://groups.google.com/group/recaptcha
|
|
||||||
*
|
|
||||||
* Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
|
|
||||||
* AUTHORS:
|
|
||||||
* Mike Crawford
|
|
||||||
* Ben Maurer
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The reCAPTCHA server URL's
|
|
||||||
*/
|
|
||||||
define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
|
|
||||||
define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
|
|
||||||
define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes the given data into a query string format
|
|
||||||
* @param $data - array of string elements to be encoded
|
|
||||||
* @return string - encoded request
|
|
||||||
*/
|
|
||||||
function _recaptcha_qsencode ($data) {
|
|
||||||
$req = "";
|
|
||||||
foreach ( $data as $key => $value )
|
|
||||||
$req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
|
|
||||||
|
|
||||||
// Cut the last '&'
|
|
||||||
$req=substr($req,0,strlen($req)-1);
|
|
||||||
return $req;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submits an HTTP POST to a reCAPTCHA server
|
|
||||||
* @param string $host
|
|
||||||
* @param string $path
|
|
||||||
* @param array $data
|
|
||||||
* @param int port
|
|
||||||
* @return array response
|
|
||||||
*/
|
|
||||||
function _recaptcha_http_post($host, $path, $data, $port = 80) {
|
|
||||||
|
|
||||||
$req = _recaptcha_qsencode ($data);
|
|
||||||
|
|
||||||
$http_request = "POST $path HTTP/1.0\r\n";
|
|
||||||
$http_request .= "Host: $host\r\n";
|
|
||||||
$http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
|
|
||||||
$http_request .= "Content-Length: " . strlen($req) . "\r\n";
|
|
||||||
$http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
|
|
||||||
$http_request .= "\r\n";
|
|
||||||
$http_request .= $req;
|
|
||||||
|
|
||||||
$response = '';
|
|
||||||
if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
|
|
||||||
die ('Could not open socket');
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite($fs, $http_request);
|
|
||||||
|
|
||||||
while ( !feof($fs) )
|
|
||||||
$response .= fgets($fs, 1160); // One TCP-IP packet
|
|
||||||
fclose($fs);
|
|
||||||
$response = explode("\r\n\r\n", $response, 2);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the challenge HTML (javascript and non-javascript version).
|
|
||||||
* This is called from the browser, and the resulting reCAPTCHA HTML widget
|
|
||||||
* is embedded within the HTML form it was called from.
|
|
||||||
* @param string $pubkey A public key for reCAPTCHA
|
|
||||||
* @param string $error The error given by reCAPTCHA (optional, default is null)
|
|
||||||
* @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
|
|
||||||
|
|
||||||
* @return string - The HTML to be embedded in the user's form.
|
|
||||||
*/
|
|
||||||
function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
|
|
||||||
{
|
|
||||||
if ($pubkey == null || $pubkey == '') {
|
|
||||||
die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($use_ssl) {
|
|
||||||
$server = RECAPTCHA_API_SECURE_SERVER;
|
|
||||||
} else {
|
|
||||||
$server = RECAPTCHA_API_SERVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
$errorpart = "";
|
|
||||||
if ($error) {
|
|
||||||
$errorpart = "&error=" . $error;
|
|
||||||
}
|
|
||||||
return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
|
|
||||||
|
|
||||||
<noscript>
|
|
||||||
<iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
|
|
||||||
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
|
|
||||||
<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
|
|
||||||
</noscript>';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReCaptchaResponse is returned from recaptcha_check_answer()
|
|
||||||
*/
|
|
||||||
class ReCaptchaResponse {
|
|
||||||
var $is_valid;
|
|
||||||
var $error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls an HTTP POST function to verify if the user's guess was correct
|
|
||||||
* @param string $privkey
|
|
||||||
* @param string $remoteip
|
|
||||||
* @param string $challenge
|
|
||||||
* @param string $response
|
|
||||||
* @param array $extra_params an array of extra variables to post to the server
|
|
||||||
* @return ReCaptchaResponse
|
|
||||||
*/
|
|
||||||
function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
|
|
||||||
{
|
|
||||||
if ($privkey == null || $privkey == '') {
|
|
||||||
die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($remoteip == null || $remoteip == '') {
|
|
||||||
die ("For security reasons, you must pass the remote ip to reCAPTCHA");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//discard spam submissions
|
|
||||||
if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
|
|
||||||
$recaptcha_response = new ReCaptchaResponse();
|
|
||||||
$recaptcha_response->is_valid = false;
|
|
||||||
$recaptcha_response->error = 'incorrect-captcha-sol';
|
|
||||||
return $recaptcha_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
|
|
||||||
array (
|
|
||||||
'privatekey' => $privkey,
|
|
||||||
'remoteip' => $remoteip,
|
|
||||||
'challenge' => $challenge,
|
|
||||||
'response' => $response
|
|
||||||
) + $extra_params
|
|
||||||
);
|
|
||||||
|
|
||||||
$answers = explode ("\n", $response [1]);
|
|
||||||
$recaptcha_response = new ReCaptchaResponse();
|
|
||||||
|
|
||||||
if (trim ($answers [0]) == 'true') {
|
|
||||||
$recaptcha_response->is_valid = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$recaptcha_response->is_valid = false;
|
|
||||||
$recaptcha_response->error = $answers [1];
|
|
||||||
}
|
|
||||||
return $recaptcha_response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gets a URL where the user can sign up for reCAPTCHA. If your application
|
|
||||||
* has a configuration page where you enter a key, you should provide a link
|
|
||||||
* using this function.
|
|
||||||
* @param string $domain The domain where the page is hosted
|
|
||||||
* @param string $appname The name of your application
|
|
||||||
*/
|
|
||||||
function recaptcha_get_signup_url ($domain = null, $appname = null) {
|
|
||||||
return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _recaptcha_aes_pad($val) {
|
|
||||||
$block_size = 16;
|
|
||||||
$numpad = $block_size - (strlen ($val) % $block_size);
|
|
||||||
return str_pad($val, strlen ($val) + $numpad, chr($numpad));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mailhide related code */
|
|
||||||
|
|
||||||
function _recaptcha_aes_encrypt($val,$ky) {
|
|
||||||
if (! function_exists ("mcrypt_encrypt")) {
|
|
||||||
die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.");
|
|
||||||
}
|
|
||||||
$mode=MCRYPT_MODE_CBC;
|
|
||||||
$enc=MCRYPT_RIJNDAEL_128;
|
|
||||||
$val=_recaptcha_aes_pad($val);
|
|
||||||
return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function _recaptcha_mailhide_urlbase64 ($x) {
|
|
||||||
return strtr(base64_encode ($x), '+/', '-_');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
|
|
||||||
function recaptcha_mailhide_url($pubkey, $privkey, $email) {
|
|
||||||
if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
|
|
||||||
die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
|
|
||||||
"you can do so at <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$ky = pack('H*', $privkey);
|
|
||||||
$cryptmail = _recaptcha_aes_encrypt ($email, $ky);
|
|
||||||
|
|
||||||
return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gets the parts of the email to expose to the user.
|
|
||||||
* eg, given johndoe@example,com return ["john", "example.com"].
|
|
||||||
* the email is then displayed as john...@example.com
|
|
||||||
*/
|
|
||||||
function _recaptcha_mailhide_email_parts ($email) {
|
|
||||||
$arr = preg_split("/@/", $email );
|
|
||||||
|
|
||||||
if (strlen ($arr[0]) <= 4) {
|
|
||||||
$arr[0] = substr ($arr[0], 0, 1);
|
|
||||||
} else if (strlen ($arr[0]) <= 6) {
|
|
||||||
$arr[0] = substr ($arr[0], 0, 3);
|
|
||||||
} else {
|
|
||||||
$arr[0] = substr ($arr[0], 0, 4);
|
|
||||||
}
|
|
||||||
return $arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets html to display an email address given a public an private key.
|
|
||||||
* to get a key, go to:
|
|
||||||
*
|
|
||||||
* http://www.google.com/recaptcha/mailhide/apikey
|
|
||||||
*/
|
|
||||||
function recaptcha_mailhide_html($pubkey, $privkey, $email) {
|
|
||||||
$emailparts = _recaptcha_mailhide_email_parts ($email);
|
|
||||||
$url = recaptcha_mailhide_url ($pubkey, $privkey, $email);
|
|
||||||
|
|
||||||
return htmlentities($emailparts[0]) . "<a href='" . htmlentities ($url) .
|
|
||||||
"' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
867
inc/locale/uk_UA/LC_MESSAGES/javascript.po
Normal file
867
inc/locale/uk_UA/LC_MESSAGES/javascript.po
Normal file
|
@ -0,0 +1,867 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2014-10-18 13:47+0200\n"
|
||||||
|
"PO-Revision-Date: 2016-06-19 17:30+0300\n"
|
||||||
|
"Language: uk\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||||
|
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"Last-Translator: kotobenko <aniguysubs@gmail.com>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"X-Generator: Poedit 1.8.8\n"
|
||||||
|
|
||||||
|
#: ../../../../js/style-select.js:40 ../../../../js/style-select.js:41
|
||||||
|
msgid "Style: "
|
||||||
|
msgstr "Стиль:"
|
||||||
|
|
||||||
|
#: ../../../../js/hide-images.js:50 ../../../../js/upload-selection.js:51
|
||||||
|
#: ../../../../js/quick-post-controls.js:30 ../../../../js/hide-images.js:51
|
||||||
|
#: ../../../../js/quick-post-controls.js:32
|
||||||
|
#: ../../../../js/upload-selection.js:61
|
||||||
|
#: ../../../../js/upload-selection.js:69
|
||||||
|
msgid "File"
|
||||||
|
msgstr "Файл"
|
||||||
|
|
||||||
|
#: ../../../../js/hide-images.js:50 ../../../../js/hide-images.js:51
|
||||||
|
msgid "hide"
|
||||||
|
msgstr "приховати"
|
||||||
|
|
||||||
|
#: ../../../../js/hide-images.js:56 ../../../../js/hide-images.js:57
|
||||||
|
#: ../../../../js/hide-images.js:63
|
||||||
|
msgid "show"
|
||||||
|
msgstr "показати"
|
||||||
|
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:39
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:54
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:40
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:55
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:41
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:56
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:53
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:70
|
||||||
|
msgid "Show locked threads"
|
||||||
|
msgstr "Показати закріплені нитки"
|
||||||
|
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:39
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:54
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:40
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:55
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:41
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:56
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:44
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:53
|
||||||
|
#: ../../../../js/toggle-locked-threads.js:70
|
||||||
|
msgid "Hide locked threads"
|
||||||
|
msgstr "Приховати закріплені нитки"
|
||||||
|
|
||||||
|
#: ../../../../js/upload-selection.js:32
|
||||||
|
#: ../../../../js/upload-selection.js:45
|
||||||
|
#: ../../../../js/upload-selection.js:53
|
||||||
|
msgid "URL"
|
||||||
|
msgstr "URL"
|
||||||
|
|
||||||
|
#: ../../../../js/upload-selection.js:50
|
||||||
|
#: ../../../../js/upload-selection.js:60
|
||||||
|
#: ../../../../js/upload-selection.js:68
|
||||||
|
msgid "Select"
|
||||||
|
msgstr "Вибрати"
|
||||||
|
|
||||||
|
#: ../../../../js/upload-selection.js:53
|
||||||
|
#: ../../../../js/upload-selection.js:63
|
||||||
|
#: ../../../../js/upload-selection.js:71
|
||||||
|
msgid "Remote"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../../../../js/upload-selection.js:56
|
||||||
|
#: ../../../../js/upload-selection.js:66
|
||||||
|
#: ../../../../js/upload-selection.js:74
|
||||||
|
msgid "Embed"
|
||||||
|
msgstr "Вбудувати"
|
||||||
|
|
||||||
|
#: ../../../../js/upload-selection.js:59
|
||||||
|
#: ../../../../js/upload-selection.js:69
|
||||||
|
#: ../../../../js/upload-selection.js:77
|
||||||
|
msgid "Oekaki"
|
||||||
|
msgstr "Оекакі"
|
||||||
|
|
||||||
|
#: ../../../../js/toggle-images.js:41 ../../../../js/toggle-images.js:42
|
||||||
|
#: ../../../../js/toggle-images.js:45
|
||||||
|
msgid "hidden"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../../../../js/toggle-images.js:57 ../../../../js/toggle-images.js:70
|
||||||
|
#: ../../../../js/toggle-images.js:58 ../../../../js/toggle-images.js:71
|
||||||
|
#: ../../../../js/toggle-images.js:86
|
||||||
|
msgid "Show images"
|
||||||
|
msgstr "Показати зображення"
|
||||||
|
|
||||||
|
#: ../../../../js/toggle-images.js:57 ../../../../js/toggle-images.js:70
|
||||||
|
#: ../../../../js/toggle-images.js:58 ../../../../js/toggle-images.js:71
|
||||||
|
#: ../../../../js/toggle-images.js:63 ../../../../js/toggle-images.js:86
|
||||||
|
msgid "Hide images"
|
||||||
|
msgstr "Приховати зображення"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-post-controls.js:27
|
||||||
|
#: ../../../../js/quick-post-controls.js:29
|
||||||
|
msgid "Password"
|
||||||
|
msgstr "Пароль"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-post-controls.js:29
|
||||||
|
#: ../../../../js/quick-post-controls.js:31
|
||||||
|
msgid "Delete file only"
|
||||||
|
msgstr "Видалити тільки файл"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-post-controls.js:31
|
||||||
|
#: ../../../../js/quick-post-controls.js:33
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Видалити"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-post-controls.js:35
|
||||||
|
#: ../../../../js/quick-post-controls.js:37
|
||||||
|
#: ../../../../js/mod/ban-list.js:40
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr "Причина"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-post-controls.js:37
|
||||||
|
#: ../../../../js/quick-post-controls.js:39
|
||||||
|
msgid "Report"
|
||||||
|
msgstr "Повідомити адміністрацію"
|
||||||
|
|
||||||
|
#: ../../../../js/expand.js:20 ../../../../js/expand.js:22
|
||||||
|
msgid "Click reply to view."
|
||||||
|
msgstr "Натисніть «Відповісти», щоб переглянути нитку повністю"
|
||||||
|
|
||||||
|
#: ../../../../js/expand.js:20 ../../../../js/expand.js:22
|
||||||
|
#: ../../../../js/live-index.js:72 ../../../../js/live-index.js:83
|
||||||
|
msgid "Click to expand"
|
||||||
|
msgstr "Показати повністю"
|
||||||
|
|
||||||
|
#: ../../../../js/expand.js:44 ../../../../js/expand.js:46
|
||||||
|
#: ../../../../js/expand.js:50
|
||||||
|
msgid "Hide expanded replies"
|
||||||
|
msgstr "Приховати розгорнуті дописи"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:10
|
||||||
|
msgid "Brush size"
|
||||||
|
msgstr "Розмір пензля"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:10
|
||||||
|
msgid "Set text"
|
||||||
|
msgstr "Додати текст"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:10
|
||||||
|
msgid "Clear"
|
||||||
|
msgstr "Очистити"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:10
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "Зберегти"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:10
|
||||||
|
msgid "Load"
|
||||||
|
msgstr "Завантажити"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:11
|
||||||
|
msgid "Toggle eraser"
|
||||||
|
msgstr "Увімкнути стирачку"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:11
|
||||||
|
msgid "Get color"
|
||||||
|
msgstr "Отримати колір"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:11
|
||||||
|
msgid "Fill"
|
||||||
|
msgstr "Залити"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:12
|
||||||
|
msgid "Use oekaki instead of file?"
|
||||||
|
msgstr "Використати оекакі замість файла?"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:21
|
||||||
|
msgid "Edit in oekaki"
|
||||||
|
msgstr "Відкрити в редакторі оекакі"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:152
|
||||||
|
msgid "Enter some text"
|
||||||
|
msgstr "Введіть текст"
|
||||||
|
|
||||||
|
#: ../../../../js/oekaki.js:153
|
||||||
|
msgid "Enter font or leave empty"
|
||||||
|
msgstr "Введіть назву шрифту або залиште порожнім"
|
||||||
|
|
||||||
|
#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:65
|
||||||
|
#: ../../../../js/forced-anon.js:69 ../../../../js/forced-anon.js:60
|
||||||
|
#: ../../../../js/forced-anon.js:66 ../../../../js/forced-anon.js:70
|
||||||
|
#: ../../../../js/forced-anon.js:61 ../../../../js/forced-anon.js:67
|
||||||
|
#: ../../../../js/forced-anon.js:71 ../../../../js/forced-anon.js:73
|
||||||
|
#: ../../../../js/forced-anon.js:81 ../../../../js/forced-anon.js:85
|
||||||
|
msgid "Forced anonymity"
|
||||||
|
msgstr "Примусова анонімність"
|
||||||
|
|
||||||
|
#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:65
|
||||||
|
#: ../../../../js/forced-anon.js:60 ../../../../js/forced-anon.js:66
|
||||||
|
#: ../../../../js/forced-anon.js:61 ../../../../js/forced-anon.js:67
|
||||||
|
#: ../../../../js/forced-anon.js:73 ../../../../js/forced-anon.js:81
|
||||||
|
msgid "enabled"
|
||||||
|
msgstr "увімкнено"
|
||||||
|
|
||||||
|
#: ../../../../js/forced-anon.js:59 ../../../../js/forced-anon.js:69
|
||||||
|
#: ../../../../js/forced-anon.js:60 ../../../../js/forced-anon.js:70
|
||||||
|
#: ../../../../js/forced-anon.js:61 ../../../../js/forced-anon.js:71
|
||||||
|
#: ../../../../js/forced-anon.js:73 ../../../../js/forced-anon.js:85
|
||||||
|
msgid "disabled"
|
||||||
|
msgstr "вимкнено"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Sun"
|
||||||
|
msgstr "НД"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Mon"
|
||||||
|
msgstr "ПН"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Tue"
|
||||||
|
msgstr "ВТ"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Wed"
|
||||||
|
msgstr "СР"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Thu"
|
||||||
|
msgstr "ЧТ"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Fri"
|
||||||
|
msgstr "ПТ"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:40 ../../../../js/local-time.js:41
|
||||||
|
#: ../../../../js/local-time.js:30 ../../../../templates/main.js:63
|
||||||
|
msgid "Sat"
|
||||||
|
msgstr "СБ"
|
||||||
|
|
||||||
|
#: ../../../../js/catalog-link.js:21 ../../../../js/catalog-link.js:32
|
||||||
|
#: ../../../../js/catalog-link.js:40 ../../../../js/catalog-link.js:33
|
||||||
|
#: ../../../../js/catalog-link.js:44 ../../../../js/catalog-link.js:52
|
||||||
|
#: ../../../../js/catalog-link.js:28 ../../../../js/catalog-link.js:39
|
||||||
|
#: ../../../../js/catalog-link.js:47
|
||||||
|
msgid "Catalog"
|
||||||
|
msgstr "Каталог"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:21 ../../../../js/quick-reply-old.js:21
|
||||||
|
#: ../../../../js/quick-reply-old.js:23
|
||||||
|
msgid "Submit"
|
||||||
|
msgstr "Розмістити"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:31 ../../../../js/quick-reply-old.js:31
|
||||||
|
#: ../../../../js/quick-reply-old.js:33
|
||||||
|
msgid "Quick reply"
|
||||||
|
msgstr "Відповісти швидко"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:33 ../../../../js/quick-reply-old.js:33
|
||||||
|
#: ../../../../js/quick-reply-old.js:35
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Posting mode: Replying to <small>>>{0}</small>"
|
||||||
|
msgstr "Режим розміщення: Відповідь <small>>>{0}</small>"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:33 ../../../../js/quick-reply-old.js:33
|
||||||
|
#: ../../../../js/quick-reply-old.js:35
|
||||||
|
msgid "Return"
|
||||||
|
msgstr "Повернутися"
|
||||||
|
|
||||||
|
#: ../../../../js/expand-all-images.js:20
|
||||||
|
#: ../../../../js/expand-all-images.js:21
|
||||||
|
#: ../../../../js/expand-all-images.js:22
|
||||||
|
#: ../../../../js/expand-all-images.js:23
|
||||||
|
msgid "Expand all images"
|
||||||
|
msgstr "Розгорнути всі зображення"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:6
|
||||||
|
msgid "Hello!"
|
||||||
|
msgstr "Привіт!"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:18
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{0} users"
|
||||||
|
msgstr "{0} користувачів"
|
||||||
|
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:28
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:39
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:29
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:40
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:52
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:63
|
||||||
|
msgid "(hide threads from this board)"
|
||||||
|
msgstr "(приховати нитки цієї дошки)"
|
||||||
|
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:32
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:44
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:33
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:45
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:56
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:68
|
||||||
|
msgid "(show threads from this board)"
|
||||||
|
msgstr "(показати нитки цієї дошки)"
|
||||||
|
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:57
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:58
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:81
|
||||||
|
msgid "No more threads to display"
|
||||||
|
msgstr "Було показано всі нитки"
|
||||||
|
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:79
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:80
|
||||||
|
#: ../../../../templates/themes/ukko/ukko.js:103
|
||||||
|
#: ../../../../js/infinite-scroll.js:48
|
||||||
|
msgid "Loading..."
|
||||||
|
msgstr "Завантаження…"
|
||||||
|
|
||||||
|
#: ../../../../js/download-original.js:32
|
||||||
|
#: ../../../../js/download-original.js:33
|
||||||
|
msgid "Save as original filename"
|
||||||
|
msgstr "Зберегти з початковим іменем файла"
|
||||||
|
|
||||||
|
#: ../../../../js/ajax-post-controls.js:43
|
||||||
|
msgid "Reported post(s)."
|
||||||
|
msgstr "Допис(и), про які повідомлено."
|
||||||
|
|
||||||
|
#: ../../../../js/ajax-post-controls.js:53
|
||||||
|
msgid "An unknown error occured!"
|
||||||
|
msgstr "Сталася невідома помилка!"
|
||||||
|
|
||||||
|
#: ../../../../js/ajax-post-controls.js:60
|
||||||
|
msgid "Something went wrong... An unknown error occured!"
|
||||||
|
msgstr "Щось пішло не так… Сталася невідома помилка!"
|
||||||
|
|
||||||
|
#: ../../../../js/ajax-post-controls.js:68
|
||||||
|
msgid "Working..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../../../../js/ajax.js:42 ../../../../js/ajax.js:45
|
||||||
|
msgid "Posting... (#%)"
|
||||||
|
msgstr "Розміщення… (#%)"
|
||||||
|
|
||||||
|
#: ../../../../js/ajax.js:104 ../../../../js/ajax.js:109
|
||||||
|
msgid "Posted..."
|
||||||
|
msgstr "Розміщено…"
|
||||||
|
|
||||||
|
#: ../../../../js/ajax.js:106 ../../../../js/ajax.js:111
|
||||||
|
msgid "An unknown error occured when posting!"
|
||||||
|
msgstr "Під час розміщення сталася невідома помилка!"
|
||||||
|
|
||||||
|
#: ../../../../js/ajax.js:130 ../../../../js/ajax.js:135
|
||||||
|
msgid "Posting..."
|
||||||
|
msgstr "Розміщення…"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:223 ../../../../js/quick-reply.js:224
|
||||||
|
#: ../../../../js/quick-reply.js:225
|
||||||
|
msgid "Upload URL"
|
||||||
|
msgstr "Адреса завантаження"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:266 ../../../../js/quick-reply.js:267
|
||||||
|
#: ../../../../js/quick-reply.js:268
|
||||||
|
msgid "Spoiler Image"
|
||||||
|
msgstr "Спойлерне зображення"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:277 ../../../../js/quick-reply.js:278
|
||||||
|
#: ../../../../js/quick-reply.js:279 ../../../../js/quick-reply.js:281
|
||||||
|
msgid "Comment"
|
||||||
|
msgstr "Коментувати"
|
||||||
|
|
||||||
|
#: ../../../../js/quick-reply.js:285 ../../../../js/quick-reply.js:406
|
||||||
|
#: ../../../../js/quick-reply.js:286 ../../../../js/quick-reply.js:407
|
||||||
|
#: ../../../../js/quick-reply.js:287 ../../../../js/quick-reply.js:408
|
||||||
|
#: ../../../../js/quick-reply.js:289 ../../../../js/quick-reply.js:410
|
||||||
|
msgid "Quick Reply"
|
||||||
|
msgstr "Швидка відповідь"
|
||||||
|
|
||||||
|
#: ../../../../js/watch.js:249 ../../../../js/watch.js:250
|
||||||
|
#: ../../../../js/watch.js:288 ../../../../js/watch.js:289
|
||||||
|
#: ../../../../js/watch.js:330 ../../../../js/watch.js:331
|
||||||
|
#: ../../../../js/watch.js:361 ../../../../js/watch.js:362
|
||||||
|
msgid "Stop watching this thread"
|
||||||
|
msgstr "Припинити стеження за ниткою"
|
||||||
|
|
||||||
|
#: ../../../../js/watch.js:249 ../../../../js/watch.js:250
|
||||||
|
#: ../../../../js/watch.js:288 ../../../../js/watch.js:289
|
||||||
|
#: ../../../../js/watch.js:330 ../../../../js/watch.js:331
|
||||||
|
#: ../../../../js/watch.js:361 ../../../../js/watch.js:362
|
||||||
|
msgid "Watch this thread"
|
||||||
|
msgstr "Стежити за ниткою"
|
||||||
|
|
||||||
|
#: ../../../../js/watch.js:260 ../../../../js/watch.js:261
|
||||||
|
#: ../../../../js/watch.js:269 ../../../../js/watch.js:299
|
||||||
|
#: ../../../../js/watch.js:300 ../../../../js/watch.js:308
|
||||||
|
#: ../../../../js/watch.js:341 ../../../../js/watch.js:342
|
||||||
|
#: ../../../../js/watch.js:350 ../../../../js/watch.js:372
|
||||||
|
#: ../../../../js/watch.js:373 ../../../../js/watch.js:381
|
||||||
|
msgid "Unpin this board"
|
||||||
|
msgstr "Відкріпити дошку"
|
||||||
|
|
||||||
|
#: ../../../../js/watch.js:260 ../../../../js/watch.js:261
|
||||||
|
#: ../../../../js/watch.js:269 ../../../../js/watch.js:299
|
||||||
|
#: ../../../../js/watch.js:300 ../../../../js/watch.js:308
|
||||||
|
#: ../../../../js/watch.js:341 ../../../../js/watch.js:342
|
||||||
|
#: ../../../../js/watch.js:350 ../../../../js/watch.js:372
|
||||||
|
#: ../../../../js/watch.js:373 ../../../../js/watch.js:381
|
||||||
|
msgid "Pin this board"
|
||||||
|
msgstr "Прикріпити дошку"
|
||||||
|
|
||||||
|
#: ../../../../js/watch.js:262 ../../../../js/watch.js:267
|
||||||
|
#: ../../../../js/watch.js:268 ../../../../js/watch.js:301
|
||||||
|
#: ../../../../js/watch.js:306 ../../../../js/watch.js:307
|
||||||
|
#: ../../../../js/watch.js:343 ../../../../js/watch.js:348
|
||||||
|
#: ../../../../js/watch.js:349 ../../../../js/watch.js:374
|
||||||
|
#: ../../../../js/watch.js:379 ../../../../js/watch.js:380
|
||||||
|
msgid "Stop watching this board"
|
||||||
|
msgstr "Не стежити за дошкою"
|
||||||
|
|
||||||
|
#: ../../../../js/watch.js:262 ../../../../js/watch.js:267
|
||||||
|
#: ../../../../js/watch.js:268 ../../../../js/watch.js:301
|
||||||
|
#: ../../../../js/watch.js:306 ../../../../js/watch.js:307
|
||||||
|
#: ../../../../js/watch.js:343 ../../../../js/watch.js:348
|
||||||
|
#: ../../../../js/watch.js:349 ../../../../js/watch.js:374
|
||||||
|
#: ../../../../js/watch.js:379 ../../../../js/watch.js:380
|
||||||
|
msgid "Watch this board"
|
||||||
|
msgstr "Стежити за дошкою"
|
||||||
|
|
||||||
|
#: ../../../../js/wpaint.js:113
|
||||||
|
msgid "Click on any image on this site to load it into oekaki applet"
|
||||||
|
msgstr ""
|
||||||
|
"Клацніть по будь-якому зображенні на сторінці, щоб завантажити його до "
|
||||||
|
"аплету оекакі"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Sunday"
|
||||||
|
msgstr "Неділя"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Monday"
|
||||||
|
msgstr "Понеділок"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Tuesday"
|
||||||
|
msgstr "Вівторок"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Wednesday"
|
||||||
|
msgstr "Середа"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Thursday"
|
||||||
|
msgstr "Четвер"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Friday"
|
||||||
|
msgstr "Пʼятниця"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:29 ../../../../templates/main.js:62
|
||||||
|
msgid "Saturday"
|
||||||
|
msgstr "Субота"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "January"
|
||||||
|
msgstr "Січень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "February"
|
||||||
|
msgstr "Лютий"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "March"
|
||||||
|
msgstr "Березень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "April"
|
||||||
|
msgstr "Квітень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../js/local-time.js:32
|
||||||
|
#: ../../../../templates/main.js:64 ../../../../templates/main.js:65
|
||||||
|
msgid "May"
|
||||||
|
msgstr "Травень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "June"
|
||||||
|
msgstr "Червень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "July"
|
||||||
|
msgstr "Липень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "August"
|
||||||
|
msgstr "Серпень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "September"
|
||||||
|
msgstr "Вересень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "October"
|
||||||
|
msgstr "Жовтень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "November"
|
||||||
|
msgstr "Листопад"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:31 ../../../../templates/main.js:64
|
||||||
|
msgid "December"
|
||||||
|
msgstr "Грудень"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Jan"
|
||||||
|
msgstr "Січ"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Feb"
|
||||||
|
msgstr "Лют"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Mar"
|
||||||
|
msgstr "Бер"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Apr"
|
||||||
|
msgstr "Кві"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Jun"
|
||||||
|
msgstr "Чер"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Jul"
|
||||||
|
msgstr "Лип"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Aug"
|
||||||
|
msgstr "Сер"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Sep"
|
||||||
|
msgstr "Вер"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Oct"
|
||||||
|
msgstr "Жов"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Nov"
|
||||||
|
msgstr "Лис"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:32 ../../../../templates/main.js:65
|
||||||
|
msgid "Dec"
|
||||||
|
msgstr "Гру"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:33 ../../../../templates/main.js:66
|
||||||
|
msgid "AM"
|
||||||
|
msgstr "AM"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:34 ../../../../templates/main.js:67
|
||||||
|
msgid "PM"
|
||||||
|
msgstr "PM"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:35 ../../../../templates/main.js:68
|
||||||
|
msgid "am"
|
||||||
|
msgstr "am"
|
||||||
|
|
||||||
|
#: ../../../../js/local-time.js:36 ../../../../templates/main.js:69
|
||||||
|
msgid "pm"
|
||||||
|
msgstr "pm"
|
||||||
|
|
||||||
|
#: ../../../../js/expand-video.js:45 ../../../../js/expand-video.js:48
|
||||||
|
msgid "Your browser does not support HTML5 video."
|
||||||
|
msgstr "Ваш браузер не підтримує відео HTML5"
|
||||||
|
|
||||||
|
#: ../../../../js/expand-video.js:189 ../../../../js/expand-video.js:192
|
||||||
|
#: ../../../../js/expand-video.js:193
|
||||||
|
msgid "[play once]"
|
||||||
|
msgstr "[відтворити раз]"
|
||||||
|
|
||||||
|
#: ../../../../js/expand-video.js:190 ../../../../js/expand-video.js:193
|
||||||
|
#: ../../../../js/expand-video.js:194
|
||||||
|
msgid "[loop]"
|
||||||
|
msgstr "[зациклити]"
|
||||||
|
|
||||||
|
#: ../../../../js/webm-settings.js:42 ../../../../js/webm-settings.js:45
|
||||||
|
msgid "WebM Settings"
|
||||||
|
msgstr "Налаштування WebM"
|
||||||
|
|
||||||
|
#: ../../../../js/webm-settings.js:44 ../../../../js/webm-settings.js:54
|
||||||
|
msgid "Expand videos inline"
|
||||||
|
msgstr "Розгорнути відео на місці"
|
||||||
|
|
||||||
|
#: ../../../../js/webm-settings.js:45 ../../../../js/webm-settings.js:55
|
||||||
|
msgid "Play videos on hover"
|
||||||
|
msgstr "Відтворити відео в пливучому віконці"
|
||||||
|
|
||||||
|
#: ../../../../js/webm-settings.js:46 ../../../../js/webm-settings.js:56
|
||||||
|
msgid "Default volume"
|
||||||
|
msgstr "Гучність за промовчанням"
|
||||||
|
|
||||||
|
#: ../../../../js/treeview.js:18
|
||||||
|
msgid "Tree view"
|
||||||
|
msgstr "Перегляд деревом"
|
||||||
|
|
||||||
|
#: ../../../../js/expand-all-images.js:32
|
||||||
|
#: ../../../../js/expand-all-images.js:35
|
||||||
|
msgid "Shrink all images"
|
||||||
|
msgstr "Позгортати всі зображення"
|
||||||
|
|
||||||
|
#: ../../../../js/no-animated-gif.js:33 ../../../../js/no-animated-gif.js:37
|
||||||
|
#: ../../../../js/no-animated-gif.js:40
|
||||||
|
msgid "Animate GIFs"
|
||||||
|
msgstr "Увімкнути анімацію GIF"
|
||||||
|
|
||||||
|
#: ../../../../js/no-animated-gif.js:42 ../../../../js/no-animated-gif.js:48
|
||||||
|
#: ../../../../js/no-animated-gif.js:47 ../../../../js/no-animated-gif.js:57
|
||||||
|
#: ../../../../js/no-animated-gif.js:62 ../../../../js/no-animated-gif.js:50
|
||||||
|
#: ../../../../js/no-animated-gif.js:60 ../../../../js/no-animated-gif.js:65
|
||||||
|
msgid "Unanimate GIFs"
|
||||||
|
msgstr "Вимкнути анімацію GIF"
|
||||||
|
|
||||||
|
#: ../../../../js/webm-settings.js:41
|
||||||
|
msgid "WebM"
|
||||||
|
msgstr "WebM"
|
||||||
|
|
||||||
|
#: ../../../../js/live-index.js:25 ../../../../js/live-index.js:84
|
||||||
|
msgid "No new posts."
|
||||||
|
msgstr "Немає нових дописів."
|
||||||
|
|
||||||
|
#: ../../../../js/live-index.js:30 ../../../../js/live-index.js:73
|
||||||
|
msgid "No new threads."
|
||||||
|
msgstr "Немає нових ниток."
|
||||||
|
|
||||||
|
#: ../../../../js/live-index.js:72
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "There are {0} new threads."
|
||||||
|
msgstr "{0} нових ниток."
|
||||||
|
|
||||||
|
#: ../../../../js/live-index.js:83
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "There are {0} new posts in this thread."
|
||||||
|
msgstr "{0} нових дописів у цій нитці."
|
||||||
|
|
||||||
|
#: ../../../../js/options.js:106
|
||||||
|
msgid "Options"
|
||||||
|
msgstr "Налаштування"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:15
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Основні"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:18
|
||||||
|
msgid "Storage: "
|
||||||
|
msgstr "Сховище:"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:21
|
||||||
|
msgid "Export"
|
||||||
|
msgstr "Експортувати"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:27
|
||||||
|
msgid "Import"
|
||||||
|
msgstr "Імпортувати"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:28
|
||||||
|
msgid "Paste your storage data"
|
||||||
|
msgstr "Вставити дані зі сховища"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:40
|
||||||
|
msgid "Erase"
|
||||||
|
msgstr "Очистити"
|
||||||
|
|
||||||
|
#: ../../../../js/options/general.js:41
|
||||||
|
msgid ""
|
||||||
|
"Are you sure you want to erase your storage? This involves your hidden "
|
||||||
|
"threads, watched threads, post password and many more."
|
||||||
|
msgstr ""
|
||||||
|
"Ви впевнені, що хочете очистити своє сховище? Списки прихованих і "
|
||||||
|
"переглянутих ниток, пароль дописування та багато іншого буде анульовано."
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-css.js:14
|
||||||
|
msgid "User CSS"
|
||||||
|
msgstr "Користувацький CSS"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-css.js:23
|
||||||
|
msgid "Update custom CSS"
|
||||||
|
msgstr "Оновити користувацький CSS"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-css.js:45
|
||||||
|
msgid "Enter here your own CSS rules..."
|
||||||
|
msgstr "Введіть власні правила CSS…"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-css.js:46
|
||||||
|
msgid ""
|
||||||
|
"If you want to make a redistributable style, be sure to\n"
|
||||||
|
" have a Yotsuba B theme selected."
|
||||||
|
msgstr ""
|
||||||
|
"Якщо хочете, щоб ваш стиль був переносним,\n"
|
||||||
|
"впевніться, що обрано тему Yotsuba B."
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-css.js:47
|
||||||
|
msgid "You can include CSS files from remote servers, for example:"
|
||||||
|
msgstr "Ви можете включати файли CSS із віддалених серверів, наприклад:"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-js.js:14
|
||||||
|
msgid "User JS"
|
||||||
|
msgstr "Користувацький JS"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-js.js:23
|
||||||
|
msgid "Update custom Javascript"
|
||||||
|
msgstr "Оновити користувацький JS"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-js.js:54
|
||||||
|
msgid "Enter here your own Javascript code..."
|
||||||
|
msgstr "Введіть власний код Javascript…"
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-js.js:55
|
||||||
|
msgid ""
|
||||||
|
"Have a backup of your storage somewhere, as messing here\n"
|
||||||
|
" may render you this website unusable."
|
||||||
|
msgstr ""
|
||||||
|
"Створіть десь в іншому місці резервну копію сховища,\n"
|
||||||
|
"бо може статись, що після змін тут ви будете не в змозі користуватися цим "
|
||||||
|
"сайтом."
|
||||||
|
|
||||||
|
#: ../../../../js/options/user-js.js:56
|
||||||
|
msgid "You can include JS files from remote servers, for example:"
|
||||||
|
msgstr "Ви можете включати файли CSS із віддалених серверів, наприклад:"
|
||||||
|
|
||||||
|
#: ../../../../js/id_colors.js:6 ../../../../js/id_colors.js:12
|
||||||
|
msgid "Color IDs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:33
|
||||||
|
msgid "Update"
|
||||||
|
msgstr "Оновлення"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:29
|
||||||
|
msgid "IP address"
|
||||||
|
msgstr "IP адреса"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:42
|
||||||
|
msgid "Seen"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:44
|
||||||
|
msgid "Message for which user was banned is included"
|
||||||
|
msgstr "Повідомлення, за яке користувача заблоковано, додається"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:45
|
||||||
|
msgid "Message:"
|
||||||
|
msgstr "Повідомлення:"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:53
|
||||||
|
msgid "Board"
|
||||||
|
msgstr "Дошка"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:55
|
||||||
|
msgid "all"
|
||||||
|
msgstr "все"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:57
|
||||||
|
msgid "Set"
|
||||||
|
msgstr "Встановити"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:58
|
||||||
|
msgid " ago"
|
||||||
|
msgstr " тому"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:61
|
||||||
|
msgid "Expires"
|
||||||
|
msgstr ""
|
||||||
|
"Сплива\n"
|
||||||
|
"є"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:62
|
||||||
|
msgid "never"
|
||||||
|
msgstr "ніколи"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:64
|
||||||
|
msgid "in "
|
||||||
|
msgstr "в"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:66
|
||||||
|
msgid "Staff"
|
||||||
|
msgstr "Модератор"
|
||||||
|
|
||||||
|
#: ../../../../js/mod/ban-list.js:73
|
||||||
|
msgid "system"
|
||||||
|
msgstr "система"
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:33
|
||||||
|
msgid "Auto"
|
||||||
|
msgstr "Авто"
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:141
|
||||||
|
msgid "Updating..."
|
||||||
|
msgstr "Оновлення…"
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:183
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Thread updated with {0} new post(s)"
|
||||||
|
msgstr "Нитку оновлено, зʼявилось {0} допис(ів)"
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:185
|
||||||
|
msgid "No new posts found"
|
||||||
|
msgstr "Нових дописів немає"
|
||||||
|
|
||||||
|
# Що мається на увазі під «pruned»?
|
||||||
|
#: ../../../../js/auto-reload.js:191
|
||||||
|
msgid "Thread deleted or pruned"
|
||||||
|
msgstr "Нитку видалено або вилучено"
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:199
|
||||||
|
msgid "Error: "
|
||||||
|
msgstr "Помилка:"
|
||||||
|
|
||||||
|
#: ../../../../js/auto-reload.js:201
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr "Невідома помилка"
|
||||||
|
|
||||||
|
#: ../../../../js/infinite-scroll.js:47
|
||||||
|
msgid "Page"
|
||||||
|
msgstr "Сторінка"
|
||||||
|
|
||||||
|
#: ../../../../js/infinite-scroll.js:82
|
||||||
|
msgid "All"
|
||||||
|
msgstr "Всі"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:29 ../../../../templates/main.js:47
|
||||||
|
msgid "second(s)"
|
||||||
|
msgstr "секунд(и)"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:31 ../../../../templates/main.js:49
|
||||||
|
msgid "minute(s)"
|
||||||
|
msgstr "хвилин(и)"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:33 ../../../../templates/main.js:51
|
||||||
|
msgid "hour(s)"
|
||||||
|
msgstr "годин(и)"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:35 ../../../../templates/main.js:53
|
||||||
|
msgid "day(s)"
|
||||||
|
msgstr "день(дні)"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:37 ../../../../templates/main.js:55
|
||||||
|
msgid "week(s)"
|
||||||
|
msgstr "тиждень(тижнів)"
|
||||||
|
|
||||||
|
#: ../../../../templates/main.js:39 ../../../../templates/main.js:57
|
||||||
|
msgid "year(s)"
|
||||||
|
msgstr "рік(років)"
|
BIN
inc/locale/uk_UA/LC_MESSAGES/tinyboard.mo
Normal file
BIN
inc/locale/uk_UA/LC_MESSAGES/tinyboard.mo
Normal file
Binary file not shown.
3542
inc/locale/uk_UA/LC_MESSAGES/tinyboard.po
Normal file
3542
inc/locale/uk_UA/LC_MESSAGES/tinyboard.po
Normal file
File diff suppressed because it is too large
Load Diff
39
inc/lock.php
Normal file
39
inc/lock.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
class Lock {
|
||||||
|
function __construct($key) { global $config;
|
||||||
|
if ($config['lock']['enabled'] == 'fs') {
|
||||||
|
$key = str_replace('/', '::', $key);
|
||||||
|
$key = str_replace("\0", '', $key);
|
||||||
|
|
||||||
|
$this->f = fopen("tmp/locks/$key", "w");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a shared lock
|
||||||
|
function get($nonblock = false) { global $config;
|
||||||
|
if ($config['lock']['enabled'] == 'fs') {
|
||||||
|
$wouldblock = false;
|
||||||
|
flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
||||||
|
if ($nonblock && $wouldblock) return false;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an exclusive lock
|
||||||
|
function get_ex($nonblock = false) { global $config;
|
||||||
|
if ($config['lock']['enabled'] == 'fs') {
|
||||||
|
$wouldblock = false;
|
||||||
|
flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
||||||
|
if ($nonblock && $wouldblock) return false;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a lock
|
||||||
|
function free() { global $config;
|
||||||
|
if ($config['lock']['enabled'] == 'fs') {
|
||||||
|
flock($this->f, LOCK_UN);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,20 +76,20 @@ function generate_salt() {
|
||||||
function login($username, $password) {
|
function login($username, $password) {
|
||||||
global $mod, $config;
|
global $mod, $config;
|
||||||
|
|
||||||
$query = prepare("SELECT `id`, `type`, `boards`, `password`, `salt` FROM ``mods`` WHERE `username` = :username");
|
$query = prepare("SELECT `id`, `type`, `boards`, `password`, `version` FROM ``mods`` WHERE BINARY `username` = :username");
|
||||||
$query->bindValue(':username', $username);
|
$query->bindValue(':username', $username);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
if ($user = $query->fetch(PDO::FETCH_ASSOC)) {
|
if ($user = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||||
list($version, $ok) = test_password($user['password'], $user['salt'], $password);
|
list($version, $ok) = test_password($user['password'], $user['version'], $password);
|
||||||
|
|
||||||
if ($ok) {
|
if ($ok) {
|
||||||
if ($config['password_crypt_version'] > $version) {
|
if ($config['password_crypt_version'] > $version) {
|
||||||
// It's time to upgrade the password hashing method!
|
// It's time to upgrade the password hashing method!
|
||||||
list ($user['salt'], $user['password']) = crypt_password($password);
|
list ($user['version'], $user['password']) = crypt_password($password);
|
||||||
$query = prepare("UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id");
|
$query = prepare("UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id");
|
||||||
$query->bindValue(':password', $user['password']);
|
$query->bindValue(':password', $user['password']);
|
||||||
$query->bindValue(':salt', $user['salt']);
|
$query->bindValue(':version', $user['version']);
|
||||||
$query->bindValue(':id', $user['id']);
|
$query->bindValue(':id', $user['id']);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ function destroyCookies() {
|
||||||
function modLog($action, $_board=null) {
|
function modLog($action, $_board=null) {
|
||||||
global $mod, $board, $config;
|
global $mod, $board, $config;
|
||||||
$query = prepare("INSERT INTO ``modlogs`` VALUES (:id, :ip, :board, :time, :text)");
|
$query = prepare("INSERT INTO ``modlogs`` VALUES (:id, :ip, :board, :time, :text)");
|
||||||
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT);
|
$query->bindValue(':id', (isset($mod['id']) ? $mod['id'] : -1), PDO::PARAM_INT);
|
||||||
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
|
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
|
||||||
$query->bindValue(':time', time(), PDO::PARAM_INT);
|
$query->bindValue(':time', time(), PDO::PARAM_INT);
|
||||||
$query->bindValue(':text', $action);
|
$query->bindValue(':text', $action);
|
||||||
|
@ -146,39 +146,6 @@ function modLog($action, $_board=null) {
|
||||||
_syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action);
|
_syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate session
|
|
||||||
|
|
||||||
if (isset($_COOKIE[$config['cookies']['mod']])) {
|
|
||||||
// Should be username:hash:salt
|
|
||||||
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]);
|
|
||||||
if (count($cookie) != 3) {
|
|
||||||
// Malformed cookies
|
|
||||||
destroyCookies();
|
|
||||||
mod_login();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM ``mods`` WHERE `username` = :username");
|
|
||||||
$query->bindValue(':username', $cookie[0]);
|
|
||||||
$query->execute() or error(db_error($query));
|
|
||||||
$user = $query->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
// validate password hash
|
|
||||||
if ($cookie[1] !== mkhash($cookie[0], $user['password'], $cookie[2])) {
|
|
||||||
// Malformed cookies
|
|
||||||
destroyCookies();
|
|
||||||
mod_login();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mod = array(
|
|
||||||
'id' => $user['id'],
|
|
||||||
'type' => $user['type'],
|
|
||||||
'username' => $cookie[0],
|
|
||||||
'boards' => explode(',', $user['boards'])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_pm_header() {
|
function create_pm_header() {
|
||||||
global $mod, $config;
|
global $mod, $config;
|
||||||
|
|
||||||
|
@ -212,4 +179,37 @@ function make_secure_link_token($uri) {
|
||||||
return substr(sha1($config['cookies']['salt'] . '-' . $uri . '-' . $mod['id']), 0, 8);
|
return substr(sha1($config['cookies']['salt'] . '-' . $uri . '-' . $mod['id']), 0, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function check_login($prompt = false) {
|
||||||
|
global $config, $mod;
|
||||||
|
// Validate session
|
||||||
|
if (isset($_COOKIE[$config['cookies']['mod']])) {
|
||||||
|
// Should be username:hash:salt
|
||||||
|
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]);
|
||||||
|
if (count($cookie) != 3) {
|
||||||
|
// Malformed cookies
|
||||||
|
destroyCookies();
|
||||||
|
if ($prompt) mod_login();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM ``mods`` WHERE `username` = :username");
|
||||||
|
$query->bindValue(':username', $cookie[0]);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
$user = $query->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// validate password hash
|
||||||
|
if ($cookie[1] !== mkhash($cookie[0], $user['password'], $cookie[2])) {
|
||||||
|
// Malformed cookies
|
||||||
|
destroyCookies();
|
||||||
|
if ($prompt) mod_login();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mod = array(
|
||||||
|
'id' => $user['id'],
|
||||||
|
'type' => $user['type'],
|
||||||
|
'username' => $cookie[0],
|
||||||
|
'boards' => explode(',', $user['boards'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ function mod_page($title, $template, $args, $subtitle = false) {
|
||||||
'hide_dashboard_link' => $template == 'mod/dashboard.html',
|
'hide_dashboard_link' => $template == 'mod/dashboard.html',
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'subtitle' => $subtitle,
|
'subtitle' => $subtitle,
|
||||||
'nojavascript' => true,
|
'boardlist' => createBoardlist($mod),
|
||||||
'body' => Element($template,
|
'body' => Element($template,
|
||||||
array_merge(
|
array_merge(
|
||||||
array('config' => $config, 'mod' => $mod),
|
array('config' => $config, 'mod' => $mod),
|
||||||
|
@ -608,7 +608,7 @@ function mod_news($page_no = 1) {
|
||||||
|
|
||||||
rebuildThemes('news');
|
rebuildThemes('news');
|
||||||
|
|
||||||
header('Location: ?/news#' . $pdo->lastInsertId(), true, $config['redirect_http']);
|
header('Location: ?/edit_news#' . $pdo->lastInsertId(), true, $config['redirect_http']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = prepare("SELECT * FROM ``news`` ORDER BY `id` DESC LIMIT :offset, :limit");
|
$query = prepare("SELECT * FROM ``news`` ORDER BY `id` DESC LIMIT :offset, :limit");
|
||||||
|
@ -621,14 +621,14 @@ function mod_news($page_no = 1) {
|
||||||
error($config['error']['404']);
|
error($config['error']['404']);
|
||||||
|
|
||||||
foreach ($news as &$entry) {
|
foreach ($news as &$entry) {
|
||||||
$entry['delete_token'] = make_secure_link_token('news/delete/' . $entry['id']);
|
$entry['delete_token'] = make_secure_link_token('edit_news/delete/' . $entry['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = prepare("SELECT COUNT(*) FROM ``news``");
|
$query = prepare("SELECT COUNT(*) FROM ``news``");
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
$count = $query->fetchColumn();
|
$count = $query->fetchColumn();
|
||||||
|
|
||||||
mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('news')));
|
mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('edit_news')));
|
||||||
}
|
}
|
||||||
|
|
||||||
function mod_news_delete($id) {
|
function mod_news_delete($id) {
|
||||||
|
@ -643,7 +643,7 @@ function mod_news_delete($id) {
|
||||||
|
|
||||||
modLog('Deleted a news entry');
|
modLog('Deleted a news entry');
|
||||||
|
|
||||||
header('Location: ?/news', true, $config['redirect_http']);
|
header('Location: ?/edit_news', true, $config['redirect_http']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mod_log($page_no = 1) {
|
function mod_log($page_no = 1) {
|
||||||
|
@ -698,6 +698,42 @@ function mod_user_log($username, $page_no = 1) {
|
||||||
mod_page(_('Moderation log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'username' => $username));
|
mod_page(_('Moderation log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'username' => $username));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mod_board_log($board, $page_no = 1, $hide_names = false, $public = false) {
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
if ($page_no < 1)
|
||||||
|
error($config['error']['404']);
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['mod_board_log'], $board) && !$public)
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
$query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board ORDER BY `time` DESC LIMIT :offset, :limit");
|
||||||
|
$query->bindValue(':board', $board);
|
||||||
|
$query->bindValue(':limit', $config['mod']['modlog_page'], PDO::PARAM_INT);
|
||||||
|
$query->bindValue(':offset', ($page_no - 1) * $config['mod']['modlog_page'], PDO::PARAM_INT);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
$logs = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (empty($logs) && $page_no > 1)
|
||||||
|
error($config['error']['404']);
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['show_ip'])) {
|
||||||
|
// Supports ipv4 only!
|
||||||
|
foreach ($logs as $i => &$log) {
|
||||||
|
$log['text'] = preg_replace_callback('/(?:<a href="\?\/IP\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}">)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:<\/a>)?/', function($matches) {
|
||||||
|
return "xxxx";//less_ip($matches[1]);
|
||||||
|
}, $log['text']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = prepare("SELECT COUNT(*) FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board");
|
||||||
|
$query->bindValue(':board', $board);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
$count = $query->fetchColumn();
|
||||||
|
|
||||||
|
mod_page(_('Board log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'board' => $board, 'hide_names' => $hide_names, 'public' => $public));
|
||||||
|
}
|
||||||
|
|
||||||
function mod_view_board($boardName, $page_no = 1) {
|
function mod_view_board($boardName, $page_no = 1) {
|
||||||
global $config, $mod;
|
global $config, $mod;
|
||||||
|
|
||||||
|
@ -846,7 +882,7 @@ function mod_page_ip($ip) {
|
||||||
|
|
||||||
$args['security_token'] = make_secure_link_token('IP/' . $ip);
|
$args['security_token'] = make_secure_link_token('IP/' . $ip);
|
||||||
|
|
||||||
mod_page(sprintf('%s: %s', _('IP'), $ip), 'mod/view_ip.html', $args, $args['hostname']);
|
mod_page(sprintf('%s: %s', _('IP'), htmlspecialchars($ip)), 'mod/view_ip.html', $args, $args['hostname']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mod_ban() {
|
function mod_ban() {
|
||||||
|
@ -1053,6 +1089,28 @@ function mod_sticky($board, $unsticky, $post) {
|
||||||
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
|
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mod_cycle($board, $uncycle, $post) {
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
if (!openBoard($board))
|
||||||
|
error($config['error']['noboard']);
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['cycle'], $board))
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
$query = prepare(sprintf('UPDATE ``posts_%s`` SET `cycle` = :cycle WHERE `id` = :id AND `thread` IS NULL', $board));
|
||||||
|
$query->bindValue(':id', $post);
|
||||||
|
$query->bindValue(':cycle', $uncycle ? 0 : 1);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
if ($query->rowCount()) {
|
||||||
|
modLog(($uncycle ? 'Made not cyclical' : 'Made cyclical') . " thread #{$post}");
|
||||||
|
buildThread($post);
|
||||||
|
buildIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
|
||||||
|
}
|
||||||
|
|
||||||
function mod_bumplock($board, $unbumplock, $post) {
|
function mod_bumplock($board, $unbumplock, $post) {
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
|
@ -1256,6 +1314,8 @@ function mod_move($originBoard, $postID) {
|
||||||
$post['has_file'] = true;
|
$post['has_file'] = true;
|
||||||
foreach ($post['files'] as $i => &$file) {
|
foreach ($post['files'] as $i => &$file) {
|
||||||
$file['file_path'] = sprintf($config['board_path'], $board['uri']) . $config['dir']['img'] . $file['file'];
|
$file['file_path'] = sprintf($config['board_path'], $board['uri']) . $config['dir']['img'] . $file['file'];
|
||||||
|
|
||||||
|
if (isset($file['thumb']))
|
||||||
$file['thumb_path'] = sprintf($config['board_path'], $board['uri']) . $config['dir']['thumb'] . $file['thumb'];
|
$file['thumb_path'] = sprintf($config['board_path'], $board['uri']) . $config['dir']['thumb'] . $file['thumb'];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1473,6 +1533,15 @@ function mod_edit_post($board, $edit_raw_html, $postID) {
|
||||||
error($config['error']['404']);
|
error($config['error']['404']);
|
||||||
|
|
||||||
if (isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['body'])) {
|
if (isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['body'])) {
|
||||||
|
// Remove any modifiers they may have put in
|
||||||
|
$_POST['body'] = remove_modifiers($_POST['body']);
|
||||||
|
|
||||||
|
// Add back modifiers in the original post
|
||||||
|
$modifiers = extract_modifiers($post['body_nomarkup']);
|
||||||
|
foreach ($modifiers as $key => $value) {
|
||||||
|
$_POST['body'] .= "<tinyboard $key>$value</tinyboard>";
|
||||||
|
}
|
||||||
|
|
||||||
if ($edit_raw_html)
|
if ($edit_raw_html)
|
||||||
$query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name, `email` = :email, `subject` = :subject, `body` = :body, `body_nomarkup` = :body_nomarkup WHERE `id` = :id', $board));
|
$query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name, `email` = :email, `subject` = :subject, `body` = :body, `body_nomarkup` = :body_nomarkup WHERE `id` = :id', $board));
|
||||||
else
|
else
|
||||||
|
@ -1501,9 +1570,14 @@ function mod_edit_post($board, $edit_raw_html, $postID) {
|
||||||
|
|
||||||
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . link_for($post) . '#' . $postID, true, $config['redirect_http']);
|
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . link_for($post) . '#' . $postID, true, $config['redirect_http']);
|
||||||
} else {
|
} else {
|
||||||
|
// Remove modifiers
|
||||||
|
$post['body_nomarkup'] = remove_modifiers($post['body_nomarkup']);
|
||||||
|
|
||||||
|
$post['body_nomarkup'] = utf8tohtml($post['body_nomarkup']);
|
||||||
|
$post['body'] = utf8tohtml($post['body']);
|
||||||
if ($config['minify_html']) {
|
if ($config['minify_html']) {
|
||||||
$post['body_nomarkup'] = str_replace("\n", '
', utf8tohtml($post['body_nomarkup']));
|
$post['body_nomarkup'] = str_replace("\n", '
', $post['body_nomarkup']);
|
||||||
$post['body'] = str_replace("\n", '
', utf8tohtml($post['body']));
|
$post['body'] = str_replace("\n", '
', $post['body']);
|
||||||
$post['body_nomarkup'] = str_replace("\r", '', $post['body_nomarkup']);
|
$post['body_nomarkup'] = str_replace("\r", '', $post['body_nomarkup']);
|
||||||
$post['body'] = str_replace("\r", '', $post['body']);
|
$post['body'] = str_replace("\r", '', $post['body']);
|
||||||
$post['body_nomarkup'] = str_replace("\t", '	', $post['body_nomarkup']);
|
$post['body_nomarkup'] = str_replace("\t", '	', $post['body_nomarkup']);
|
||||||
|
@ -1650,6 +1724,8 @@ function mod_deletebyip($boardName, $post, $global = false) {
|
||||||
|
|
||||||
rebuildThemes('post-delete', $board['uri']);
|
rebuildThemes('post-delete', $board['uri']);
|
||||||
|
|
||||||
|
buildIndex();
|
||||||
|
|
||||||
if ($post['thread'])
|
if ($post['thread'])
|
||||||
$threads_to_rebuild[$post['board']][$post['thread']] = true;
|
$threads_to_rebuild[$post['board']][$post['thread']] = true;
|
||||||
else
|
else
|
||||||
|
@ -1734,12 +1810,12 @@ function mod_user($uid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_POST['password'] != '') {
|
if ($_POST['password'] != '') {
|
||||||
list($salt, $password) = crypt_password($_POST['password']);
|
list($version, $password) = crypt_password($_POST['password']);
|
||||||
|
|
||||||
$query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id');
|
$query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id');
|
||||||
$query->bindValue(':id', $uid);
|
$query->bindValue(':id', $uid);
|
||||||
$query->bindValue(':password', $password);
|
$query->bindValue(':password', $password);
|
||||||
$query->bindValue(':salt', $salt);
|
$query->bindValue(':version', $version);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
modLog('Changed password for ' . utf8tohtml($_POST['username']) . ' <small>(#' . $user['id'] . ')</small>');
|
modLog('Changed password for ' . utf8tohtml($_POST['username']) . ' <small>(#' . $user['id'] . ')</small>');
|
||||||
|
@ -1760,12 +1836,12 @@ function mod_user($uid) {
|
||||||
|
|
||||||
if (hasPermission($config['mod']['change_password']) && $uid == $mod['id'] && isset($_POST['password'])) {
|
if (hasPermission($config['mod']['change_password']) && $uid == $mod['id'] && isset($_POST['password'])) {
|
||||||
if ($_POST['password'] != '') {
|
if ($_POST['password'] != '') {
|
||||||
list($salt, $password) = crypt_password($_POST['password']);
|
list($version, $password) = crypt_password($_POST['password']);
|
||||||
|
|
||||||
$query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id');
|
$query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id');
|
||||||
$query->bindValue(':id', $uid);
|
$query->bindValue(':id', $uid);
|
||||||
$query->bindValue(':password', $password);
|
$query->bindValue(':password', $password);
|
||||||
$query->bindValue(':salt', $salt);
|
$query->bindValue(':version', $version);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
modLog('Changed own password');
|
modLog('Changed own password');
|
||||||
|
@ -1832,12 +1908,12 @@ function mod_user_new() {
|
||||||
if (!isset($config['mod']['groups'][$type]) || $type == DISABLED)
|
if (!isset($config['mod']['groups'][$type]) || $type == DISABLED)
|
||||||
error(sprintf($config['error']['invalidfield'], 'type'));
|
error(sprintf($config['error']['invalidfield'], 'type'));
|
||||||
|
|
||||||
list($salt, $password) = crypt_password($_POST['password']);
|
list($version, $password) = crypt_password($_POST['password']);
|
||||||
|
|
||||||
$query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, :type, :boards)');
|
$query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :version, :type, :boards)');
|
||||||
$query->bindValue(':username', $_POST['username']);
|
$query->bindValue(':username', $_POST['username']);
|
||||||
$query->bindValue(':password', $password);
|
$query->bindValue(':password', $password);
|
||||||
$query->bindValue(':salt', $salt);
|
$query->bindValue(':version', $version);
|
||||||
$query->bindValue(':type', $type);
|
$query->bindValue(':type', $type);
|
||||||
$query->bindValue(':boards', implode(',', $boards));
|
$query->bindValue(':boards', implode(',', $boards));
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
@ -2560,7 +2636,7 @@ function mod_theme_uninstall($theme_name) {
|
||||||
|
|
||||||
// Clean cache
|
// Clean cache
|
||||||
Cache::delete("themes");
|
Cache::delete("themes");
|
||||||
Cache::delete("theme_settings_".$theme);
|
Cache::delete("theme_settings_".$theme_name);
|
||||||
|
|
||||||
header('Location: ?/themes', true, $config['redirect_http']);
|
header('Location: ?/themes', true, $config['redirect_http']);
|
||||||
}
|
}
|
||||||
|
@ -2578,6 +2654,167 @@ function mod_theme_rebuild($theme_name) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This needs to be done for `secure` CSRF prevention compatibility, otherwise the $board will be read in as the token if editing global pages.
|
||||||
|
function delete_page_base($page = '', $board = false) {
|
||||||
|
global $config, $mod;
|
||||||
|
|
||||||
|
if (empty($board))
|
||||||
|
$board = false;
|
||||||
|
|
||||||
|
if (!$board && $mod['boards'][0] !== '*')
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['edit_pages'], $board))
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
if ($board !== FALSE && !openBoard($board))
|
||||||
|
error($config['error']['noboard']);
|
||||||
|
|
||||||
|
if ($board) {
|
||||||
|
$query = prepare('DELETE FROM ``pages`` WHERE `board` = :board AND `name` = :name');
|
||||||
|
$query->bindValue(':board', ($board ? $board : NULL));
|
||||||
|
} else {
|
||||||
|
$query = prepare('DELETE FROM ``pages`` WHERE `board` IS NULL AND `name` = :name');
|
||||||
|
}
|
||||||
|
$query->bindValue(':name', $page);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
header('Location: ?/edit_pages' . ($board ? ('/' . $board) : ''), true, $config['redirect_http']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mod_delete_page($page = '') {
|
||||||
|
delete_page_base($page);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mod_delete_page_board($page = '', $board = false) {
|
||||||
|
delete_page_base($page, $board);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mod_edit_page($id) {
|
||||||
|
global $config, $mod, $board;
|
||||||
|
|
||||||
|
$query = prepare('SELECT * FROM ``pages`` WHERE `id` = :id');
|
||||||
|
$query->bindValue(':id', $id);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
$page = $query->fetch();
|
||||||
|
|
||||||
|
if (!$page)
|
||||||
|
error(_('Could not find the page you are trying to edit.'));
|
||||||
|
|
||||||
|
if (!$page['board'] && $mod['boards'][0] !== '*')
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['edit_pages'], $page['board']))
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
if ($page['board'] && !openBoard($page['board']))
|
||||||
|
error($config['error']['noboard']);
|
||||||
|
|
||||||
|
if (isset($_POST['method'], $_POST['content'])) {
|
||||||
|
$content = $_POST['content'];
|
||||||
|
$method = $_POST['method'];
|
||||||
|
$page['type'] = $method;
|
||||||
|
|
||||||
|
if (!in_array($method, array('markdown', 'html', 'infinity')))
|
||||||
|
error(_('Unrecognized page markup method.'));
|
||||||
|
|
||||||
|
switch ($method) {
|
||||||
|
case 'markdown':
|
||||||
|
$write = markdown($content);
|
||||||
|
break;
|
||||||
|
case 'html':
|
||||||
|
if (hasPermission($config['mod']['rawhtml'])) {
|
||||||
|
$write = $content;
|
||||||
|
} else {
|
||||||
|
$write = purify_html($content);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'infinity':
|
||||||
|
$c = $content;
|
||||||
|
markup($content);
|
||||||
|
$write = $content;
|
||||||
|
$content = $c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($write) or !$write)
|
||||||
|
error(_('Failed to mark up your input for some reason...'));
|
||||||
|
|
||||||
|
$query = prepare('UPDATE ``pages`` SET `type` = :method, `content` = :content WHERE `id` = :id');
|
||||||
|
$query->bindValue(':method', $method);
|
||||||
|
$query->bindValue(':content', $content);
|
||||||
|
$query->bindValue(':id', $id);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
$fn = ($board['uri'] ? ($board['uri'] . '/') : '') . $page['name'] . '.html';
|
||||||
|
$body = "<div class='ban'>$write</div>";
|
||||||
|
$html = Element('page.html', array('config' => $config, 'body' => $body, 'title' => utf8tohtml($page['title'])));
|
||||||
|
file_write($fn, $html);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($content)) {
|
||||||
|
$query = prepare('SELECT `content` FROM ``pages`` WHERE `id` = :id');
|
||||||
|
$query->bindValue(':id', $id);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
$content = $query->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_page(sprintf(_('Editing static page: %s'), $page['name']), 'mod/edit_page.html', array('page' => $page, 'token' => make_secure_link_token("edit_page/$id"), 'content' => prettify_textarea($content), 'board' => $board));
|
||||||
|
}
|
||||||
|
|
||||||
|
function mod_pages($board = false) {
|
||||||
|
global $config, $mod, $pdo;
|
||||||
|
|
||||||
|
if (empty($board))
|
||||||
|
$board = false;
|
||||||
|
|
||||||
|
if (!$board && $mod['boards'][0] !== '*')
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['edit_pages'], $board))
|
||||||
|
error($config['error']['noaccess']);
|
||||||
|
|
||||||
|
if ($board !== FALSE && !openBoard($board))
|
||||||
|
error($config['error']['noboard']);
|
||||||
|
|
||||||
|
if ($board) {
|
||||||
|
$query = prepare('SELECT * FROM ``pages`` WHERE `board` = :board');
|
||||||
|
$query->bindValue(':board', $board);
|
||||||
|
} else {
|
||||||
|
$query = query('SELECT * FROM ``pages`` WHERE `board` IS NULL');
|
||||||
|
}
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
$pages = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (isset($_POST['page'])) {
|
||||||
|
if ($board and sizeof($pages) > $config['pages_max'])
|
||||||
|
error(sprintf(_('Sorry, this site only allows %d pages per board.'), $config['pages_max']));
|
||||||
|
|
||||||
|
if (!preg_match('/^[a-z0-9]{1,255}$/', $_POST['page']))
|
||||||
|
error(_('Page names must be < 255 chars and may only contain lowercase letters A-Z and digits 1-9.'));
|
||||||
|
|
||||||
|
foreach ($pages as $i => $p) {
|
||||||
|
if ($_POST['page'] === $p['name'])
|
||||||
|
error(_('Refusing to create a new page with the same name as an existing one.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = ($_POST['title'] ? $_POST['title'] : NULL);
|
||||||
|
|
||||||
|
$query = prepare('INSERT INTO ``pages``(board, title, name) VALUES(:board, :title, :name)');
|
||||||
|
$query->bindValue(':board', ($board ? $board : NULL));
|
||||||
|
$query->bindValue(':title', $title);
|
||||||
|
$query->bindValue(':name', $_POST['page']);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
$pages[] = array('id' => $pdo->lastInsertId(), 'name' => $_POST['page'], 'board' => $board, 'title' => $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pages as $i => &$p) {
|
||||||
|
$p['delete_token'] = make_secure_link_token('edit_pages/delete/' . $p['name'] . ($board ? ('/' . $board) : ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_page(_('Pages'), 'mod/pages.html', array('pages' => $pages, 'token' => make_secure_link_token('edit_pages' . ($board ? ('/' . $board) : '')), 'board' => $board));
|
||||||
|
}
|
||||||
|
|
||||||
function mod_debug_antispam() {
|
function mod_debug_antispam() {
|
||||||
global $pdo, $config;
|
global $pdo, $config;
|
||||||
|
|
||||||
|
@ -2694,3 +2931,4 @@ function mod_debug_apc() {
|
||||||
|
|
||||||
mod_page(_('Debug: APC'), 'mod/debug/apc.html', array('cached_vars' => $cached_vars));
|
mod_page(_('Debug: APC'), 'mod/debug/apc.html', array('cached_vars' => $cached_vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
152
inc/nntpchan/nntpchan.php
Normal file
152
inc/nntpchan/nntpchan.php
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 vichan-devel
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('TINYBOARD') or exit;
|
||||||
|
|
||||||
|
function gen_msgid($board, $id) {
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$b = preg_replace("/[^0-9a-zA-Z$]/", 'x', $board);
|
||||||
|
$salt = sha1($board . "|" . $id . "|" . $config['nntpchan']['salt']);
|
||||||
|
$salt = substr($salt, 0, 7);
|
||||||
|
$salt = base_convert($salt, 16, 36);
|
||||||
|
|
||||||
|
return "<$b.$id.$salt@".$config['nntpchan']['domain'].">";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function gen_nntp($headers, $files) {
|
||||||
|
if (count($files) == 0) {
|
||||||
|
}
|
||||||
|
else if (count($files) == 1 && $files[0]['type'] == 'text/plain') {
|
||||||
|
$content = $files[0]['text'] . "\r\n";
|
||||||
|
$headers['Content-Type'] = "text/plain; charset=UTF-8";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$boundary = sha1($headers['Message-Id']);
|
||||||
|
$content = "";
|
||||||
|
$headers['Content-Type'] = "multipart/mixed; boundary=$boundary";
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$content .= "--$boundary\r\n";
|
||||||
|
if (isset($file['name'])) {
|
||||||
|
$file['name'] = preg_replace('/[\r\n\0"]/', '', $file['name']);
|
||||||
|
$content .= "Content-Disposition: form-data; filename=\"$file[name]\"; name=\"attachment\"\r\n";
|
||||||
|
}
|
||||||
|
$type = explode('/', $file['type'])[0];
|
||||||
|
if ($type == 'text') {
|
||||||
|
$file['type'] .= '; charset=UTF-8';
|
||||||
|
}
|
||||||
|
$content .= "Content-Type: $file[type]\r\n";
|
||||||
|
if ($type != 'text' && $type != 'message') {
|
||||||
|
$file['text'] = base64_encode($file['text']);
|
||||||
|
$content .= "Content-Transfer-Encoding: base64\r\n";
|
||||||
|
}
|
||||||
|
$content .= "\r\n";
|
||||||
|
$content .= $file['text'];
|
||||||
|
$content .= "\r\n";
|
||||||
|
}
|
||||||
|
$content .= "--$boundary--\r\n";
|
||||||
|
|
||||||
|
$headers['Mime-Version'] = '1.0';
|
||||||
|
}
|
||||||
|
//$headers['Content-Length'] = strlen($content);
|
||||||
|
$headers['Date'] = date('r', $headers['Date']);
|
||||||
|
$out = "";
|
||||||
|
foreach ($headers as $id => $val) {
|
||||||
|
$val = str_replace("\n", "\n\t", $val);
|
||||||
|
$out .= "$id: $val\r\n";
|
||||||
|
}
|
||||||
|
$out .= "\r\n";
|
||||||
|
$out .= $content;
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nntp_publish($msg, $id) {
|
||||||
|
global $config;
|
||||||
|
$server = $config["nntpchan"]["server"];
|
||||||
|
$s = fsockopen("tcp://$server");
|
||||||
|
fgets($s);
|
||||||
|
fputs($s, "MODE STREAM\r\n");
|
||||||
|
fgets($s);
|
||||||
|
fputs($s, "TAKETHIS $id\r\n");
|
||||||
|
fputs($s, $msg);
|
||||||
|
fputs($s, "\r\n.\r\n");
|
||||||
|
fgets($s);
|
||||||
|
fputs($s, "QUIT\r\n");
|
||||||
|
fclose($s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function post2nntp($post, $msgid) {
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$headers = array();
|
||||||
|
$files = array();
|
||||||
|
|
||||||
|
$headers['Message-Id'] = $msgid;
|
||||||
|
$headers['Newsgroups'] = $config['nntpchan']['group'];
|
||||||
|
$headers['Date'] = time();
|
||||||
|
$headers['Subject'] = $post['subject'] ? $post['subject'] : "None";
|
||||||
|
$headers['From'] = $post['name'] . " <poster@" . $config['nntpchan']['domain'] . ">";
|
||||||
|
|
||||||
|
if ($post['email'] == 'sage') {
|
||||||
|
$headers['X-Sage'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$post['op']) {
|
||||||
|
// Get muh parent
|
||||||
|
$query = prepare("SELECT `message_id` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id");
|
||||||
|
$query->bindValue(':board', $post['board']);
|
||||||
|
$query->bindValue(':id', $post['thread']);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
if ($result = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
$headers['References'] = $result['message_id'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false; // We don't have OP. Discarding.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's parse the body a bit.
|
||||||
|
$body = trim($post['body_nomarkup']);
|
||||||
|
$body = preg_replace('/\r?\n/', "\r\n", $body);
|
||||||
|
$body = preg_replace_callback('@>>(>/([a-zA-Z0-9_+-]+)/)?([0-9]+)@', function($o) use ($post) {
|
||||||
|
if ($o[1]) {
|
||||||
|
$board = $o[2];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$board = $post['board'];
|
||||||
|
}
|
||||||
|
$id = $o[3];
|
||||||
|
|
||||||
|
$query = prepare("SELECT `message_id_digest` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id");
|
||||||
|
$query->bindValue(':board', $board);
|
||||||
|
$query->bindValue(':id', $id);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
if ($result = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
return ">>".substr($result['message_id_digest'], 0, 18);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $o[0]; // Should send URL imo
|
||||||
|
}
|
||||||
|
}, $body);
|
||||||
|
$body = preg_replace('/>>>>([0-9a-fA-F])+/', '>>\1', $body);
|
||||||
|
|
||||||
|
|
||||||
|
$files[] = array('type' => 'text/plain', 'text' => $body);
|
||||||
|
|
||||||
|
foreach ($post['files'] as $id => $file) {
|
||||||
|
$fc = array();
|
||||||
|
|
||||||
|
$fc['type'] = $file['type'];
|
||||||
|
$fc['text'] = file_get_contents($file['file_path']);
|
||||||
|
$fc['name'] = $file['name'];
|
||||||
|
|
||||||
|
$files[] = $fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($headers, $files);
|
||||||
|
}
|
30
inc/nntpchan/tests.php
Normal file
30
inc/nntpchan/tests.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
define('TINYBOARD', 'fuck yeah');
|
||||||
|
require_once('nntpchan.php');
|
||||||
|
|
||||||
|
|
||||||
|
die();
|
||||||
|
|
||||||
|
$time = time();
|
||||||
|
echo "\n@@@@ Thread:\n";
|
||||||
|
echo $m0 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.0000.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None"],
|
||||||
|
[['type' => 'text/plain', 'text' => "THIS IS A NEW TEST THREAD"]]);
|
||||||
|
echo "\n@@@@ Single msg:\n";
|
||||||
|
echo $m1 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1234.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
|
||||||
|
[['type' => 'text/plain', 'text' => "hello world, with no image :("]]);
|
||||||
|
echo "\n@@@@ Single msg and pseudoimage:\n";
|
||||||
|
echo $m2 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.2137.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
|
||||||
|
[['type' => 'text/plain', 'text' => "hello world, now with an image!"],
|
||||||
|
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"]]);
|
||||||
|
echo "\n@@@@ Single msg and two pseudoimages:\n";
|
||||||
|
echo $m3 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1488.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
|
||||||
|
[['type' => 'text/plain', 'text' => "hello world, now WITH TWO IMAGES!!!"],
|
||||||
|
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"],
|
||||||
|
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif2.gif"]]);
|
||||||
|
shoveitup($m0, "<1234.0000.".$time."@example.vichan.net>");
|
||||||
|
sleep(1);
|
||||||
|
shoveitup($m1, "<1234.1234.".$time."@example.vichan.net>");
|
||||||
|
sleep(1);
|
||||||
|
shoveitup($m2, "<1234.2137.".$time."@example.vichan.net>");
|
||||||
|
shoveitup($m3, "<1234.1488.".$time."@example.vichan.net>");
|
||||||
|
|
28
inc/polyfill.php
Normal file
28
inc/polyfill.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// PHP 5.4
|
||||||
|
|
||||||
|
if (!function_exists('hex2bin')) {
|
||||||
|
function hex2bin($data) {
|
||||||
|
return pack("H*" , $hex_string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHP 5.6
|
||||||
|
|
||||||
|
if (!function_exists('hash_equals')) {
|
||||||
|
function hash_equals($ours, $theirs) {
|
||||||
|
$ours = (string)$ours;
|
||||||
|
$theirs = (string)$theirs;
|
||||||
|
|
||||||
|
$tlen = strlen($theirs);
|
||||||
|
$olen = strlen($ours);
|
||||||
|
|
||||||
|
$answer = 0;
|
||||||
|
for ($i = 0; $i < $tlen; $i++) {
|
||||||
|
$answer |= ord($ours[$olen > $i ? $i : 0]) ^ ord($theirs[$i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $answer === 0 && $olen === $tlen;
|
||||||
|
}
|
||||||
|
}
|
49
inc/queue.php
Normal file
49
inc/queue.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Queue {
|
||||||
|
function __construct($key) { global $config;
|
||||||
|
if ($config['queue']['enabled'] == 'fs') {
|
||||||
|
$this->lock = new Lock($key);
|
||||||
|
$key = str_replace('/', '::', $key);
|
||||||
|
$key = str_replace("\0", '', $key);
|
||||||
|
$this->key = "tmp/queue/$key/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function push($str) { global $config;
|
||||||
|
if ($config['queue']['enabled'] == 'fs') {
|
||||||
|
$this->lock->get_ex();
|
||||||
|
file_put_contents($this->key.microtime(true), $str);
|
||||||
|
$this->lock->free();
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pop($n = 1) { global $config;
|
||||||
|
if ($config['queue']['enabled'] == 'fs') {
|
||||||
|
$this->lock->get_ex();
|
||||||
|
$dir = opendir($this->key);
|
||||||
|
$paths = array();
|
||||||
|
while ($n > 0) {
|
||||||
|
$path = readdir($dir);
|
||||||
|
if ($path === FALSE) break;
|
||||||
|
elseif ($path == '.' || $path == '..') continue;
|
||||||
|
else { $paths[] = $path; $n--; }
|
||||||
|
}
|
||||||
|
$out = array();
|
||||||
|
foreach ($paths as $v) {
|
||||||
|
$out []= file_get_contents($this->key.$v);
|
||||||
|
unlink($this->key.$v);
|
||||||
|
}
|
||||||
|
$this->lock->free();
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use the constructor. Use the get_queue function.
|
||||||
|
$queues = array();
|
||||||
|
|
||||||
|
function get_queue($name) { global $queues;
|
||||||
|
return $queues[$name] = isset ($queues[$name]) ? $queues[$name] : new Queue($name);
|
||||||
|
}
|
65
inc/route.php
Normal file
65
inc/route.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// vichan's routing mechanism
|
||||||
|
|
||||||
|
// don't bother with that unless you use smart build or advanced build
|
||||||
|
// you can use those parts for your own implementations though :^)
|
||||||
|
|
||||||
|
defined('TINYBOARD') or exit;
|
||||||
|
|
||||||
|
function route($path) { global $config;
|
||||||
|
$entrypoints = array();
|
||||||
|
|
||||||
|
$entrypoints['/%b/'] = 'sb_board';
|
||||||
|
$entrypoints['/%b/'.$config['file_index']] = 'sb_board';
|
||||||
|
$entrypoints['/%b/'.$config['file_page']] = 'sb_board';
|
||||||
|
$entrypoints['/%b/%d.json'] = 'sb_api_board';
|
||||||
|
if ($config['api']['enabled']) {
|
||||||
|
$entrypoints['/%b/threads.json'] = 'sb_api';
|
||||||
|
$entrypoints['/%b/catalog.json'] = 'sb_api';
|
||||||
|
}
|
||||||
|
|
||||||
|
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck';
|
||||||
|
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50';
|
||||||
|
if ($config['slugify']) {
|
||||||
|
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck';
|
||||||
|
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50';
|
||||||
|
}
|
||||||
|
if ($config['api']['enabled']) {
|
||||||
|
$entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread';
|
||||||
|
}
|
||||||
|
|
||||||
|
$entrypoints['/*/'] = 'sb_ukko';
|
||||||
|
$entrypoints['/*/index.html'] = 'sb_ukko';
|
||||||
|
$entrypoints['/recent.html'] = 'sb_recent';
|
||||||
|
$entrypoints['/%b/catalog.html'] = 'sb_catalog';
|
||||||
|
$entrypoints['/%b/index.rss'] = 'sb_catalog';
|
||||||
|
$entrypoints['/sitemap.xml'] = 'sb_sitemap';
|
||||||
|
|
||||||
|
$entrypoints = array_merge($entrypoints, $config['controller_entrypoints']);
|
||||||
|
|
||||||
|
$reached = false;
|
||||||
|
|
||||||
|
list($request) = explode('?', $path);
|
||||||
|
|
||||||
|
foreach ($entrypoints as $id => $fun) {
|
||||||
|
$id = '@^' . preg_quote($id, '@') . '$@u';
|
||||||
|
|
||||||
|
$id = str_replace('%b', '('.$config['board_regex'].')', $id);
|
||||||
|
$id = str_replace('%d', '([0-9]+)', $id);
|
||||||
|
$id = str_replace('%s', '[a-zA-Z0-9-]+', $id);
|
||||||
|
|
||||||
|
$matches = null;
|
||||||
|
|
||||||
|
if (preg_match ($id, $request, $matches)) {
|
||||||
|
array_shift($matches);
|
||||||
|
|
||||||
|
$reached = array($fun, $matches);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reached;
|
||||||
|
}
|
||||||
|
|
144
install.php
144
install.php
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Installation/upgrade file
|
// Installation/upgrade file
|
||||||
define('VERSION', '5.0.0');
|
define('VERSION', '5.1.4');
|
||||||
|
|
||||||
require 'inc/functions.php';
|
require 'inc/functions.php';
|
||||||
|
|
||||||
|
@ -556,6 +556,45 @@ if (file_exists($config['has_installed'])) {
|
||||||
case '4.9.93':
|
case '4.9.93':
|
||||||
query('ALTER TABLE ``mods`` CHANGE `password` `password` VARCHAR(255) NOT NULL;') or error(db_error());
|
query('ALTER TABLE ``mods`` CHANGE `password` `password` VARCHAR(255) NOT NULL;') or error(db_error());
|
||||||
query('ALTER TABLE ``mods`` CHANGE `salt` `salt` VARCHAR(64) NOT NULL;') or error(db_error());
|
query('ALTER TABLE ``mods`` CHANGE `salt` `salt` VARCHAR(64) NOT NULL;') or error(db_error());
|
||||||
|
case '5.0.0':
|
||||||
|
query('ALTER TABLE ``mods`` CHANGE `salt` `version` VARCHAR(64) NOT NULL;') or error(db_error());
|
||||||
|
case '5.0.1':
|
||||||
|
case '5.1.0':
|
||||||
|
query('CREATE TABLE IF NOT EXISTS ``pages`` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`board` varchar(255) DEFAULT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`title` varchar(255) DEFAULT NULL,
|
||||||
|
`type` varchar(255) DEFAULT NULL,
|
||||||
|
`content` text,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `u_pages` (`name`,`board`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;') or error(db_error());
|
||||||
|
case '5.1.1':
|
||||||
|
foreach ($boards as &$board) {
|
||||||
|
query(sprintf("ALTER TABLE ``posts_%s`` ADD `cycle` int(1) NOT NULL AFTER `locked`", $board['uri'])) or error(db_error());
|
||||||
|
}
|
||||||
|
case '5.1.2':
|
||||||
|
query('CREATE TABLE IF NOT EXISTS ``nntp_references`` (
|
||||||
|
`board` varchar(60) NOT NULL,
|
||||||
|
`id` int(11) unsigned NOT NULL,
|
||||||
|
`message_id` varchar(255) CHARACTER SET ascii NOT NULL,
|
||||||
|
`message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL,
|
||||||
|
`own` tinyint(1) NOT NULL,
|
||||||
|
`headers` text,
|
||||||
|
PRIMARY KEY (`message_id_digest`),
|
||||||
|
UNIQUE KEY `message_id` (`message_id`),
|
||||||
|
UNIQUE KEY `u_board_id` (`board`, `id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
') or error(db_error());
|
||||||
|
case '5.1.3':
|
||||||
|
query('CREATE TABLE IF NOT EXISTS ``captchas`` (
|
||||||
|
`cookie` varchar(50),
|
||||||
|
`extra` varchar(200),
|
||||||
|
`text` varchar(255),
|
||||||
|
`created_at` int(11),
|
||||||
|
PRIMARY KEY (`cookie`,`extra`),
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;') or error(db_error());
|
||||||
case false:
|
case false:
|
||||||
// TODO: enhance Tinyboard -> vichan upgrade path.
|
// TODO: enhance Tinyboard -> vichan upgrade path.
|
||||||
query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error());
|
query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error());
|
||||||
|
@ -579,6 +618,27 @@ if (file_exists($config['has_installed'])) {
|
||||||
die(Element('page.html', $page));
|
die(Element('page.html', $page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function create_config_from_array(&$instance_config, &$array, $prefix = '') {
|
||||||
|
foreach ($array as $name => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
$instance_config .= "\n";
|
||||||
|
create_config_from_array($instance_config, $value, $prefix . '[\'' . addslashes($name) . '\']');
|
||||||
|
$instance_config .= "\n";
|
||||||
|
} else {
|
||||||
|
$instance_config .= ' $config' . $prefix . '[\'' . addslashes($name) . '\'] = ';
|
||||||
|
|
||||||
|
if (is_numeric($value))
|
||||||
|
$instance_config .= $value;
|
||||||
|
else
|
||||||
|
$instance_config .= "'" . addslashes($value) . "'";
|
||||||
|
|
||||||
|
$instance_config .= ";\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
if ($step == 0) {
|
if ($step == 0) {
|
||||||
// Agreeement
|
// Agreeement
|
||||||
$page['body'] = '
|
$page['body'] = '
|
||||||
|
@ -612,7 +672,7 @@ if ($step == 0) {
|
||||||
'installed' => extension_loaded('pdo'),
|
'installed' => extension_loaded('pdo'),
|
||||||
'required' => true
|
'required' => true
|
||||||
),
|
),
|
||||||
'PDO' => array(
|
'GD' => array(
|
||||||
'installed' => extension_loaded('gd'),
|
'installed' => extension_loaded('gd'),
|
||||||
'required' => true
|
'required' => true
|
||||||
),
|
),
|
||||||
|
@ -625,17 +685,17 @@ if ($step == 0) {
|
||||||
$tests = array(
|
$tests = array(
|
||||||
array(
|
array(
|
||||||
'category' => 'PHP',
|
'category' => 'PHP',
|
||||||
'name' => 'PHP ≥ 5.3',
|
'name' => 'PHP ≥ 5.4',
|
||||||
'result' => PHP_VERSION_ID >= 50300,
|
'result' => PHP_VERSION_ID >= 50400,
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'message' => 'vichan requires PHP 5.3 or better.',
|
'message' => 'vichan requires PHP 5.4 or better.',
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'PHP',
|
'category' => 'PHP',
|
||||||
'name' => 'PHP ≥ 5.4',
|
'name' => 'PHP ≥ 5.6',
|
||||||
'result' => PHP_VERSION_ID >= 50400,
|
'result' => PHP_VERSION_ID >= 50600,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'message' => 'vichan works best on PHP 5.4 or better.',
|
'message' => 'vichan works best on PHP 5.6 or better.',
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'PHP',
|
'category' => 'PHP',
|
||||||
|
@ -692,6 +752,7 @@ if ($step == 0) {
|
||||||
'result' => $can_exec && shell_exec('which convert'),
|
'result' => $can_exec && shell_exec('which convert'),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'message' => '(Optional) `convert` was not found or executable; command-line ImageMagick image processing cannot be enabled.',
|
'message' => '(Optional) `convert` was not found or executable; command-line ImageMagick image processing cannot be enabled.',
|
||||||
|
'effect' => function (&$config) { $config['thumb_method'] = 'convert'; },
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'Image processing',
|
'category' => 'Image processing',
|
||||||
|
@ -706,6 +767,7 @@ if ($step == 0) {
|
||||||
'result' => $can_exec && shell_exec('which gm'),
|
'result' => $can_exec && shell_exec('which gm'),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'message' => '(Optional) `gm` was not found or executable; command-line GraphicsMagick (faster than ImageMagick) cannot be enabled.',
|
'message' => '(Optional) `gm` was not found or executable; command-line GraphicsMagick (faster than ImageMagick) cannot be enabled.',
|
||||||
|
'effect' => function (&$config) { $config['thumb_method'] = 'gm'; },
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'Image processing',
|
'category' => 'Image processing',
|
||||||
|
@ -713,13 +775,25 @@ if ($step == 0) {
|
||||||
'result' => $can_exec && shell_exec('which gifsicle'),
|
'result' => $can_exec && shell_exec('which gifsicle'),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'message' => '(Optional) `gifsicle` was not found or executable; you may not use `convert+gifsicle` for better animated GIF thumbnailing.',
|
'message' => '(Optional) `gifsicle` was not found or executable; you may not use `convert+gifsicle` for better animated GIF thumbnailing.',
|
||||||
|
'effect' => function (&$config) { if ($config['thumb_method'] == 'gm') $config['thumb_method'] = 'gm+gifsicle';
|
||||||
|
if ($config['thumb_method'] == 'convert') $config['thumb_method'] = 'convert+gifsicle'; },
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'Image processing',
|
'category' => 'Image processing',
|
||||||
'name' => '`md5sum` (quick file hashing)',
|
'name' => '`md5sum` (quick file hashing on GNU/Linux)',
|
||||||
|
'prereq' => '',
|
||||||
'result' => $can_exec && shell_exec('echo "vichan" | md5sum') == "141225c362da02b5c359c45b665168de -\n",
|
'result' => $can_exec && shell_exec('echo "vichan" | md5sum') == "141225c362da02b5c359c45b665168de -\n",
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower.',
|
'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower. Ignore if not using Linux.',
|
||||||
|
'effect' => function (&$config) { $config['gnu_md5'] = true; },
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'category' => 'Image processing',
|
||||||
|
'name' => '`/sbin/md5` (quick file hashing on BSDs)',
|
||||||
|
'result' => $can_exec && shell_exec('echo "vichan" | /sbin/md5 -r') == "141225c362da02b5c359c45b665168de\n",
|
||||||
|
'required' => false,
|
||||||
|
'message' => '(Optional) `/sbin/md5` was not found or executable; file hashing for multiple images will be slower. Ignore if not using BSD.',
|
||||||
|
'effect' => function (&$config) { $config['bsd_md5'] = true; },
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'File permissions',
|
'category' => 'File permissions',
|
||||||
|
@ -735,6 +809,13 @@ if ($step == 0) {
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'message' => 'You must give vichan permission to create (and write to) the <code>templates/cache</code> directory or performance will be drastically reduced.'
|
'message' => 'You must give vichan permission to create (and write to) the <code>templates/cache</code> directory or performance will be drastically reduced.'
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'category' => 'File permissions',
|
||||||
|
'name' => getcwd() . '/tmp/cache',
|
||||||
|
'result' => is_dir('tmp/cache') && is_writable('tmp/cache'),
|
||||||
|
'required' => true,
|
||||||
|
'message' => 'You must give vichan permission to write to the <code>tmp/cache</code> directory.'
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'category' => 'File permissions',
|
'category' => 'File permissions',
|
||||||
'name' => getcwd() . '/inc/instance-config.php',
|
'name' => getcwd() . '/inc/instance-config.php',
|
||||||
|
@ -761,14 +842,24 @@ if ($step == 0) {
|
||||||
|
|
||||||
$config['font_awesome'] = true;
|
$config['font_awesome'] = true;
|
||||||
|
|
||||||
|
$additional_config = array();
|
||||||
|
foreach ($tests as $test) {
|
||||||
|
if ($test['result'] && isset($test['effect'])) {
|
||||||
|
$test['effect']($additional_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$more = '';
|
||||||
|
create_config_from_array($more, $additional_config);
|
||||||
|
$_SESSION['more'] = $more;
|
||||||
|
|
||||||
echo Element('page.html', array(
|
echo Element('page.html', array(
|
||||||
'body' => Element('installer/check-requirements.html', array(
|
'body' => Element('installer/check-requirements.html', array(
|
||||||
'extensions' => $extensions,
|
'extensions' => $extensions,
|
||||||
'tests' => $tests,
|
'tests' => $tests,
|
||||||
'config' => $config
|
'config' => $config,
|
||||||
)),
|
)),
|
||||||
'title' => 'Checking environment',
|
'title' => 'Checking environment',
|
||||||
'config' => $config
|
'config' => $config,
|
||||||
));
|
));
|
||||||
} elseif ($step == 2) {
|
} elseif ($step == 2) {
|
||||||
// Basic config
|
// Basic config
|
||||||
|
@ -779,14 +870,18 @@ if ($step == 0) {
|
||||||
|
|
||||||
echo Element('page.html', array(
|
echo Element('page.html', array(
|
||||||
'body' => Element('installer/config.html', array(
|
'body' => Element('installer/config.html', array(
|
||||||
'config' => $config
|
'config' => $config,
|
||||||
|
'more' => $_SESSION['more'],
|
||||||
)),
|
)),
|
||||||
'title' => 'Configuration',
|
'title' => 'Configuration',
|
||||||
'config' => $config
|
'config' => $config
|
||||||
));
|
));
|
||||||
} elseif ($step == 3) {
|
} elseif ($step == 3) {
|
||||||
|
$more = $_POST['more'];
|
||||||
|
unset($_POST['more']);
|
||||||
|
|
||||||
$instance_config =
|
$instance_config =
|
||||||
'<?php
|
'<'.'?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instance Configuration
|
* Instance Configuration
|
||||||
|
@ -798,27 +893,10 @@ if ($step == 0) {
|
||||||
|
|
||||||
';
|
';
|
||||||
|
|
||||||
function create_config_from_array(&$instance_config, &$array, $prefix = '') {
|
|
||||||
foreach ($array as $name => $value) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
$instance_config .= "\n";
|
|
||||||
create_config_from_array($instance_config, $value, $prefix . '[\'' . addslashes($name) . '\']');
|
|
||||||
$instance_config .= "\n";
|
|
||||||
} else {
|
|
||||||
$instance_config .= ' $config' . $prefix . '[\'' . addslashes($name) . '\'] = ';
|
|
||||||
|
|
||||||
if (is_numeric($value))
|
|
||||||
$instance_config .= $value;
|
|
||||||
else
|
|
||||||
$instance_config .= "'" . addslashes($value) . "'";
|
|
||||||
|
|
||||||
$instance_config .= ";\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
create_config_from_array($instance_config, $_POST);
|
create_config_from_array($instance_config, $_POST);
|
||||||
|
|
||||||
|
$instance_config .= "\n";
|
||||||
|
$instance_config .= $more;
|
||||||
$instance_config .= "\n";
|
$instance_config .= "\n";
|
||||||
|
|
||||||
if (@file_put_contents('inc/instance-config.php', $instance_config)) {
|
if (@file_put_contents('inc/instance-config.php', $instance_config)) {
|
||||||
|
|
56
install.sql
56
install.sql
|
@ -65,6 +65,7 @@ CREATE TABLE IF NOT EXISTS `boards` (
|
||||||
`uri` varchar(58) CHARACTER SET utf8 NOT NULL,
|
`uri` varchar(58) CHARACTER SET utf8 NOT NULL,
|
||||||
`title` tinytext NOT NULL,
|
`title` tinytext NOT NULL,
|
||||||
`subtitle` tinytext,
|
`subtitle` tinytext,
|
||||||
|
-- `indexed` boolean default true,
|
||||||
PRIMARY KEY (`uri`)
|
PRIMARY KEY (`uri`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ CREATE TABLE IF NOT EXISTS `mods` (
|
||||||
`id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
|
`id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`username` varchar(30) NOT NULL,
|
`username` varchar(30) NOT NULL,
|
||||||
`password` varchar(256) CHARACTER SET ascii NOT NULL COMMENT 'SHA256',
|
`password` varchar(256) CHARACTER SET ascii NOT NULL COMMENT 'SHA256',
|
||||||
`salt` varchar(64) CHARACTER SET ascii NOT NULL,
|
`version` varchar(64) CHARACTER SET ascii NOT NULL,
|
||||||
`type` smallint(2) NOT NULL,
|
`type` smallint(2) NOT NULL,
|
||||||
`boards` text CHARACTER SET utf8 NOT NULL,
|
`boards` text CHARACTER SET utf8 NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
|
@ -244,7 +245,7 @@ CREATE TABLE IF NOT EXISTS `search_queries` (
|
||||||
`ip` varchar(39) NOT NULL,
|
`ip` varchar(39) NOT NULL,
|
||||||
`time` int(11) NOT NULL,
|
`time` int(11) NOT NULL,
|
||||||
`query` text NOT NULL
|
`query` text NOT NULL
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
@ -296,6 +297,57 @@ CREATE TABLE IF NOT EXISTS `ban_appeals` (
|
||||||
KEY `ban_id` (`ban_id`)
|
KEY `ban_id` (`ban_id`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `pages`
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `pages` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`board` varchar(58) CHARACTER SET utf8 DEFAULT NULL,
|
||||||
|
`name` varchar(255) CHARACTER SET utf8 NOT NULL,
|
||||||
|
`title` varchar(255) DEFAULT NULL,
|
||||||
|
`type` varchar(255) DEFAULT NULL,
|
||||||
|
`content` text,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `u_pages` (`name`,`board`)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `nntp_references`
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `nntp_references` (
|
||||||
|
`board` varchar(30) NOT NULL,
|
||||||
|
`id` int(11) unsigned NOT NULL,
|
||||||
|
`message_id` varchar(255) CHARACTER SET ascii NOT NULL,
|
||||||
|
`message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL,
|
||||||
|
`own` tinyint(1) NOT NULL,
|
||||||
|
`headers` text,
|
||||||
|
PRIMARY KEY (`message_id_digest`),
|
||||||
|
UNIQUE KEY `message_id` (`message_id`),
|
||||||
|
UNIQUE KEY `u_board_id` (`board`, `id`)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `captchas`
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `captchas` (
|
||||||
|
`cookie` VARCHAR(50),
|
||||||
|
`extra` VARCHAR(200),
|
||||||
|
`text` VARCHAR(255),
|
||||||
|
`created_at` INT(11),
|
||||||
|
PRIMARY KEY (`cookie`,`extra`)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
|
||||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
|
||||||
|
|
27
js/auto-scroll.js
Normal file
27
js/auto-scroll.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
$('document').ready(function () {
|
||||||
|
var autoScroll = localStorage['autoScroll'] ? true : false;
|
||||||
|
if (window.Options && Options.get_tab('general')){
|
||||||
|
Options.extend_tab('general','<label id=\'autoScroll\'><input type=\'checkbox\' />' + ' Scroll to new posts' + '</label>');
|
||||||
|
$('#autoScroll').find('input').prop('checked', autoScroll);
|
||||||
|
}
|
||||||
|
$('#autoScroll').on('change', function() {
|
||||||
|
if(autoScroll) {
|
||||||
|
delete localStorage.autoScroll;
|
||||||
|
} else {
|
||||||
|
localStorage.autoScroll = true;
|
||||||
|
}
|
||||||
|
autoScroll =! autoScroll
|
||||||
|
if(active_page == 'thread')
|
||||||
|
$('input.auto-scroll').prop('checked', autoScroll);
|
||||||
|
});
|
||||||
|
if (active_page == 'thread') {
|
||||||
|
$('span[id="updater"]').children('a').after(' (<input class="auto-scroll" type="checkbox"></input> Scroll to New posts)');
|
||||||
|
$('input.auto-scroll').prop('checked', autoScroll);
|
||||||
|
$(document).on('new_post', function (e, post) {
|
||||||
|
if ($('input.auto-scroll').prop('checked'))
|
||||||
|
{
|
||||||
|
scrollTo(0, $(post).offset().top - window.innerHeight + $(post).outerHeight(true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
43
js/captcha.js
Normal file
43
js/captcha.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
var tout;
|
||||||
|
|
||||||
|
function redo_events(provider, extra) {
|
||||||
|
$('.captcha .captcha_text, textarea[id="body"]').off("focus").one("focus", function() { actually_load_captcha(provider, extra); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function actually_load_captcha(provider, extra) {
|
||||||
|
$('.captcha .captcha_text, textarea[id="body"]').off("focus");
|
||||||
|
|
||||||
|
if (tout !== undefined) {
|
||||||
|
clearTimeout(tout);
|
||||||
|
}
|
||||||
|
|
||||||
|
$.getJSON(provider, {mode: 'get', extra: extra}, function(json) {
|
||||||
|
$(".captcha .captcha_cookie").val(json.cookie);
|
||||||
|
$(".captcha .captcha_html").html(json.captchahtml);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
redo_events(provider, extra);
|
||||||
|
}, json.expires_in * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function load_captcha(provider, extra) {
|
||||||
|
$(function() {
|
||||||
|
$(".captcha>td").html("<input class='captcha_text' type='text' name='captcha_text' size='32' maxlength='6' autocomplete='off'>"+
|
||||||
|
"<input class='captcha_cookie' name='captcha_cookie' type='hidden'>"+
|
||||||
|
"<div class='captcha_html'></div>");
|
||||||
|
|
||||||
|
$("#quick-reply .captcha .captcha_text").prop("placeholder", _("Verification"));
|
||||||
|
|
||||||
|
$(".captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); });
|
||||||
|
$(document).on("ajax_after_post", function() { actually_load_captcha(provider, extra); });
|
||||||
|
redo_events(provider, extra);
|
||||||
|
|
||||||
|
$(window).on("quick-reply", function() {
|
||||||
|
redo_events(provider, extra);
|
||||||
|
$("#quick-reply .captcha .captcha_html").html($("form:not(#quick-reply) .captcha .captcha_html").html());
|
||||||
|
$("#quick-reply .captcha .captcha_cookie").val($("form:not(#quick-reply) .captcha .captcha_cookie").html());
|
||||||
|
$("#quick-reply .captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -34,16 +34,16 @@ if (active_page == 'catalog') {
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchToggle() {
|
function searchToggle() {
|
||||||
var button = $('#catalog_search_button')[0];
|
var button = $('#catalog_search_button');
|
||||||
|
|
||||||
if (!button.dataset.expanded) {
|
if (!button.data('expanded')) {
|
||||||
button.dataset.expanded = '1';
|
button.data('expanded', '1');
|
||||||
button.innerText = 'Close';
|
button.text('Close');
|
||||||
$('.catalog_search').append(' <input id="search_field" style="border: inset 1px;">');
|
$('.catalog_search').append(' <input id="search_field" style="border: inset 1px;">');
|
||||||
$('#search_field').focus();
|
$('#search_field').focus();
|
||||||
} else {
|
} else {
|
||||||
delete button.dataset.expanded;
|
button.removeData('expanded');
|
||||||
button.innerText = 'Search';
|
button.text('Search');
|
||||||
$('.catalog_search').children().last().remove();
|
$('.catalog_search').children().last().remove();
|
||||||
$('div[id="Grid"]>.mix').each(function () { $(this).css('display', 'inline-block'); });
|
$('div[id="Grid"]>.mix').each(function () { $(this).css('display', 'inline-block'); });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
if (active_page == 'catalog') $(function(){
|
if (active_page == 'catalog') $(function(){
|
||||||
|
if (localStorage.catalog !== undefined) {
|
||||||
|
var catalog = JSON.parse(localStorage.catalog);
|
||||||
|
} else {
|
||||||
|
var catalog = {};
|
||||||
|
localStorage.catalog = JSON.stringify(catalog);
|
||||||
|
}
|
||||||
|
|
||||||
$("#sort_by").change(function(){
|
$("#sort_by").change(function(){
|
||||||
var value = this.value;
|
var value = this.value;
|
||||||
$("#sort-"+value).trigger("click");
|
$('#Grid').mixItUp('sort', (value == "random" ? value : "sticky:desc " + value));
|
||||||
|
catalog.sort_by = value;
|
||||||
|
localStorage.catalog = JSON.stringify(catalog);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#image_size").change(function(){
|
$("#image_size").change(function(){
|
||||||
|
@ -11,9 +19,30 @@ if (active_page == 'catalog') $(function(){
|
||||||
$(".grid-li").removeClass("grid-size-small");
|
$(".grid-li").removeClass("grid-size-small");
|
||||||
$(".grid-li").removeClass("grid-size-large");
|
$(".grid-li").removeClass("grid-size-large");
|
||||||
$(".grid-li").addClass("grid-size-"+value);
|
$(".grid-li").addClass("grid-size-"+value);
|
||||||
|
catalog.image_size = value;
|
||||||
|
localStorage.catalog = JSON.stringify(catalog);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#Grid').mixitup({
|
$('#Grid').mixItUp({
|
||||||
|
animation: {
|
||||||
|
enable: false
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (catalog.sort_by !== undefined) {
|
||||||
|
$('#sort_by').val(catalog.sort_by).trigger('change');
|
||||||
|
}
|
||||||
|
if (catalog.image_size !== undefined) {
|
||||||
|
$('#image_size').val(catalog.image_size).trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('div.thread').on('click', function(e) {
|
||||||
|
if ($(this).css('overflow-y') === 'hidden') {
|
||||||
|
$(this).css('overflow-y', 'auto');
|
||||||
|
$(this).css('width', '100%');
|
||||||
|
} else {
|
||||||
|
$(this).css('overflow-y', 'hidden');
|
||||||
|
$(this).css('width', 'auto');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
383
js/comment-toolbar.js
Normal file
383
js/comment-toolbar.js
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
/*
|
||||||
|
* comment-toolbar.js
|
||||||
|
* - Adds a toolbar above the commenting area containing most of 8Chan's formatting options
|
||||||
|
* - Press Esc to close quick-reply window when it's in focus
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
||||||
|
* $config['additional_javascript'][] = 'js/comment-toolbar.js';
|
||||||
|
*/
|
||||||
|
if (active_page == 'thread' || active_page == 'index') {
|
||||||
|
var formatText = (function($){
|
||||||
|
"use strict";
|
||||||
|
var self = {};
|
||||||
|
self.rules = {
|
||||||
|
spoiler: {
|
||||||
|
text: _('Spoiler'),
|
||||||
|
key: 's',
|
||||||
|
multiline: false,
|
||||||
|
exclusiveline: false,
|
||||||
|
prefix:'**',
|
||||||
|
suffix:'**'
|
||||||
|
},
|
||||||
|
italics: {
|
||||||
|
text: _('Italics'),
|
||||||
|
key: 'i',
|
||||||
|
multiline: false,
|
||||||
|
exclusiveline: false,
|
||||||
|
prefix: "''",
|
||||||
|
suffix: "''"
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
text: _('Bold'),
|
||||||
|
key: 'b',
|
||||||
|
multiline: false,
|
||||||
|
exclusiveline: false,
|
||||||
|
prefix: "'''",
|
||||||
|
suffix: "'''"
|
||||||
|
},
|
||||||
|
underline: {
|
||||||
|
text: _('Underline'),
|
||||||
|
key: 'u',
|
||||||
|
multiline: false,
|
||||||
|
exclusiveline: false,
|
||||||
|
prefix:'__',
|
||||||
|
suffix:'__'
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
text: _('Code'),
|
||||||
|
key: 'f',
|
||||||
|
multiline: true,
|
||||||
|
exclusiveline: false,
|
||||||
|
prefix: '[code]',
|
||||||
|
suffix: '[/code]'
|
||||||
|
},
|
||||||
|
strike: {
|
||||||
|
text: _('Strike'),
|
||||||
|
key: 'd',
|
||||||
|
multiline:false,
|
||||||
|
exclusiveline:false,
|
||||||
|
prefix:'~~',
|
||||||
|
suffix:'~~'
|
||||||
|
},
|
||||||
|
heading: {
|
||||||
|
text: _('Heading'),
|
||||||
|
key: 'r',
|
||||||
|
multiline:false,
|
||||||
|
exclusiveline:true,
|
||||||
|
prefix:'==',
|
||||||
|
suffix:'=='
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.toolbar_wrap = function(node) {
|
||||||
|
var parent = $(node).parents('form[name="post"]');
|
||||||
|
self.wrap(parent.find('#body')[0],'textarea[name="body"]', parent.find('.format-text > select')[0].value, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.wrap = function(ref, target, option, expandedwrap) {
|
||||||
|
// clean and validate arguments
|
||||||
|
if (ref == null) return;
|
||||||
|
var settings = {multiline: false, exclusiveline: false, prefix:'', suffix: null};
|
||||||
|
$.extend(settings,JSON.parse(localStorage.formatText_rules)[option]);
|
||||||
|
|
||||||
|
// resolve targets into array of proper node elements
|
||||||
|
// yea, this is overly verbose, oh well.
|
||||||
|
var res = [];
|
||||||
|
if (target instanceof Array) {
|
||||||
|
for (var indexa in target) {
|
||||||
|
if (target.hasOwnProperty(indexa)) {
|
||||||
|
if (typeof target[indexa] == 'string') {
|
||||||
|
var nodes = $(target[indexa]);
|
||||||
|
for (var indexb in nodes) {
|
||||||
|
if (indexa.hasOwnProperty(indexb)) res.push(nodes[indexb]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push(target[indexa]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof target == 'string') {
|
||||||
|
var nodes = $(target);
|
||||||
|
for (var index in nodes) {
|
||||||
|
if (nodes.hasOwnProperty(index)) res.push(nodes[index]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target = res;
|
||||||
|
//record scroll top to restore it later.
|
||||||
|
var scrollTop = ref.scrollTop;
|
||||||
|
|
||||||
|
//We will restore the selection later, so record the current selection
|
||||||
|
var selectionStart = ref.selectionStart;
|
||||||
|
var selectionEnd = ref.selectionEnd;
|
||||||
|
|
||||||
|
var text = ref.value;
|
||||||
|
var before = text.substring(0, selectionStart);
|
||||||
|
var selected = text.substring(selectionStart, selectionEnd);
|
||||||
|
var after = text.substring(selectionEnd);
|
||||||
|
var whiteSpace = [" ","\t"];
|
||||||
|
var breakSpace = ["\r","\n"];
|
||||||
|
var cursor;
|
||||||
|
|
||||||
|
// handles multiline selections on formatting that doesn't support spanning over multiple lines
|
||||||
|
if (!settings.multiline) selected = selected.replace(/(\r|\n|\r\n)/g,settings.suffix +"$1"+ settings.prefix);
|
||||||
|
|
||||||
|
// handles formatting that requires it to be on it's own line OR if the user wishes to expand the wrap to the nearest linebreak
|
||||||
|
if (settings.exclusiveline || expandedwrap) {
|
||||||
|
// buffer the begining of the selection until a linebreak
|
||||||
|
cursor = before.length -1;
|
||||||
|
while (cursor >= 0 && breakSpace.indexOf(before.charAt(cursor)) == -1) {
|
||||||
|
cursor--;
|
||||||
|
}
|
||||||
|
selected = before.substring(cursor +1) + selected;
|
||||||
|
before = before.substring(0, cursor +1);
|
||||||
|
|
||||||
|
// buffer the end of the selection until a linebreak
|
||||||
|
cursor = 0;
|
||||||
|
while (cursor < after.length && breakSpace.indexOf(after.charAt(cursor)) == -1) {
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
selected += after.substring(0, cursor);
|
||||||
|
after = after.substring(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set values
|
||||||
|
var res = before + settings.prefix + selected + settings.suffix + after;
|
||||||
|
$(target).val(res);
|
||||||
|
|
||||||
|
// restore the selection area and scroll of the reference
|
||||||
|
ref.selectionEnd = before.length + settings.prefix.length + selected.length;
|
||||||
|
if (selectionStart === selectionEnd) {
|
||||||
|
ref.selectionStart = ref.selectionEnd;
|
||||||
|
} else {
|
||||||
|
ref.selectionStart = before.length + settings.prefix.length;
|
||||||
|
}
|
||||||
|
ref.scrollTop = scrollTop;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.build_toolbars = function(){
|
||||||
|
if (localStorage.formatText_toolbar == 'true'){
|
||||||
|
// remove existing toolbars
|
||||||
|
if ($('.format-text').length > 0) $('.format-text').remove();
|
||||||
|
|
||||||
|
// Place toolbar above each textarea input
|
||||||
|
var name, options = '', rules = JSON.parse(localStorage.formatText_rules);
|
||||||
|
for (var index in rules) {
|
||||||
|
if (!rules.hasOwnProperty(index)) continue;
|
||||||
|
name = rules[index].text;
|
||||||
|
|
||||||
|
//add hint if key exists
|
||||||
|
if (rules[index].key) {
|
||||||
|
name += ' (CTRL + '+ rules[index].key.toUpperCase() +')';
|
||||||
|
}
|
||||||
|
options += '<option value="'+ index +'">'+ name +'</option>';
|
||||||
|
}
|
||||||
|
$('[name="body"]').before('<div class="format-text"><a href="javascript:;" onclick="formatText.toolbar_wrap(this);">Wrap</a><select>'+ options +'</select></div>');
|
||||||
|
$('body').append('<style>#quick-reply .format-text>a{width:15%;display:inline-block;text-align:center;}#quick-reply .format-text>select{width:85%;};</style>');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.add_rule = function(rule, index){
|
||||||
|
if (rule === undefined) rule = {
|
||||||
|
text: 'New Rule',
|
||||||
|
key: '',
|
||||||
|
multiline:false,
|
||||||
|
exclusiveline:false,
|
||||||
|
prefix:'',
|
||||||
|
suffix:''
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate an id for the rule
|
||||||
|
if (index === undefined) {
|
||||||
|
var rules = JSON.parse(localStorage.formatText_rules);
|
||||||
|
while (rules[index] || index === undefined) {
|
||||||
|
index = ''
|
||||||
|
index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1);
|
||||||
|
index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1);
|
||||||
|
index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (window.Options && Options.get_tab('formatting')){
|
||||||
|
var html = $('<div class="format_rule" name="'+ index +'"></div>').html('\
|
||||||
|
<input type="text" name="text" class="format_option" size="10" value=\"'+ rule.text.replace(/"/g, '"') +'\">\
|
||||||
|
<input type="checkbox" name="multiline" class="format_option" '+ (rule.multiline ? 'checked' : '') +'>\
|
||||||
|
<input type="checkbox" name="exclusiveline" class="format_option" '+ (rule.exclusiveline ? 'checked' : '') +'>\
|
||||||
|
<input type="text" name="prefix" class="format_option" size="8" value=\"'+ (rule.prefix ? rule.prefix.replace(/"/g, '"') : '') +'\">\
|
||||||
|
<input type="text" name="suffix" class="format_option" size="8" value=\"'+ (rule.suffix ? rule.suffix.replace(/"/g, '"') : '') +'\">\
|
||||||
|
<input type="text" name="key" class="format_option" size="2" maxlength="1" value=\"'+ rule.key +'\">\
|
||||||
|
<input type="button" value="X" onclick="if(confirm(\'Do you wish to remove the '+ rule.text +' formatting rule?\'))$(this).parent().remove();">\
|
||||||
|
');
|
||||||
|
|
||||||
|
if ($('.format_rule').length > 0) {
|
||||||
|
$('.format_rule').last().after(html);
|
||||||
|
} else {
|
||||||
|
Options.extend_tab('formatting', html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.save_rules = function(){
|
||||||
|
var rule, newrules = {}, rules = $('.format_rule');
|
||||||
|
for (var index=0;rules[index];index++) {
|
||||||
|
rule = $(rules[index]);
|
||||||
|
newrules[rule.attr('name')] = {
|
||||||
|
text: rule.find('[name="text"]').val(),
|
||||||
|
key: rule.find('[name="key"]').val(),
|
||||||
|
prefix: rule.find('[name="prefix"]').val(),
|
||||||
|
suffix: rule.find('[name="suffix"]').val(),
|
||||||
|
multiline: rule.find('[name="multiline"]').is(':checked'),
|
||||||
|
exclusiveline: rule.find('[name="exclusiveline"]').is(':checked')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
localStorage.formatText_rules = JSON.stringify(newrules);
|
||||||
|
self.build_toolbars();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.reset_rules = function(to_default) {
|
||||||
|
$('.format_rule').remove();
|
||||||
|
var rules;
|
||||||
|
if (to_default) rules = self.rules;
|
||||||
|
else rules = JSON.parse(localStorage.formatText_rules);
|
||||||
|
for (var index in rules){
|
||||||
|
if (!rules.hasOwnProperty(index)) continue;
|
||||||
|
self.add_rule(rules[index], index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// setup default rules for customizing
|
||||||
|
if (!localStorage.formatText_rules) localStorage.formatText_rules = JSON.stringify(self.rules);
|
||||||
|
|
||||||
|
// setup code to be ran when page is ready (work around for main.js compilation).
|
||||||
|
$(document).ready(function(){
|
||||||
|
// Add settings to Options panel general tab
|
||||||
|
if (window.Options && Options.get_tab('general')) {
|
||||||
|
var s1 = '#formatText_keybinds>input', s2 = '#formatText_toolbar>input', e = 'change';
|
||||||
|
Options.extend_tab('general', '\
|
||||||
|
<fieldset>\
|
||||||
|
<legend>Formatting Options</legend>\
|
||||||
|
<label id="formatText_keybinds"><input type="checkbox">' + _('Enable formatting keybinds') + '</label>\
|
||||||
|
<label id="formatText_toolbar"><input type="checkbox">' + _('Show formatting toolbar') + '</label>\
|
||||||
|
</fieldset>\
|
||||||
|
');
|
||||||
|
} else {
|
||||||
|
var s1 = '#formatText_keybinds', s2 = '#formatText_toolbar', e = 'click';
|
||||||
|
$('hr:first').before('<div id="formatText_keybinds" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Enable formatting keybinds') +'</a></div>');
|
||||||
|
$('hr:first').before('<div id="formatText_toolbar" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Show formatting toolbar') +'</a></div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the tab for customizing the format settings
|
||||||
|
if (window.Options && !Options.get_tab('formatting')) {
|
||||||
|
Options.add_tab('formatting', 'angle-right', _('Customize Formatting'));
|
||||||
|
Options.extend_tab('formatting', '\
|
||||||
|
<style>\
|
||||||
|
.format_option{\
|
||||||
|
margin-right:5px;\
|
||||||
|
overflow:initial;\
|
||||||
|
font-size:15px;\
|
||||||
|
}\
|
||||||
|
.format_option[type="text"]{\
|
||||||
|
text-align:center;\
|
||||||
|
padding-bottom: 2px;\
|
||||||
|
padding-top: 2px;\
|
||||||
|
}\
|
||||||
|
.format_option:last-child{\
|
||||||
|
margin-right:0;\
|
||||||
|
}\
|
||||||
|
fieldset{\
|
||||||
|
margin-top:5px;\
|
||||||
|
}\
|
||||||
|
</style>\
|
||||||
|
');
|
||||||
|
|
||||||
|
// Data control row
|
||||||
|
Options.extend_tab('formatting', '\
|
||||||
|
<button onclick="formatText.add_rule();">'+_('Add Rule')+'</button>\
|
||||||
|
<button onclick="formatText.save_rules();">'+_('Save Rules')+'</button>\
|
||||||
|
<button onclick="formatText.reset_rules(false);">'+_('Revert')+'</button>\
|
||||||
|
<button onclick="formatText.reset_rules(true);">'+_('Reset to Default')+'</button>\
|
||||||
|
');
|
||||||
|
|
||||||
|
// Descriptor row
|
||||||
|
Options.extend_tab('formatting', '\
|
||||||
|
<span class="format_option" style="margin-left:25px;">Name</span>\
|
||||||
|
<span class="format_option" style="margin-left:45px;" title="Multi-line: Allow formatted area to contain linebreaks.">ML</span>\
|
||||||
|
<span class="format_option" style="margin-left:0px;" title="Exclusive-line: Require formatted area to start after and end before a linebreak.">EL</span>\
|
||||||
|
<span class="format_option" style="margin-left:25px;" title="Text injected at the start of a format area.">Prefix</span>\
|
||||||
|
<span class="format_option" style="margin-left:60px;" title="Text injected at the end of a format area.">Suffix</span>\
|
||||||
|
<span class="format_option" style="margin-left:40px;" title="Optional keybind value to allow keyboard shortcut access.">Key</span>\
|
||||||
|
');
|
||||||
|
|
||||||
|
// Rule rows
|
||||||
|
var rules = JSON.parse(localStorage.formatText_rules);
|
||||||
|
for (var index in rules){
|
||||||
|
if (!rules.hasOwnProperty(index)) continue;
|
||||||
|
self.add_rule(rules[index], index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting for enabling formatting keybinds
|
||||||
|
$(s1).on(e, function(e) {
|
||||||
|
console.log('Keybind');
|
||||||
|
if (!localStorage.formatText_keybinds || localStorage.formatText_keybinds == 'false') {
|
||||||
|
localStorage.formatText_keybinds = 'true';
|
||||||
|
if (window.Options && Options.get_tab('general')) e.target.checked = true;
|
||||||
|
} else {
|
||||||
|
localStorage.formatText_keybinds = 'false';
|
||||||
|
if (window.Options && Options.get_tab('general')) e.target.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// setting for toolbar injection
|
||||||
|
$(s2).on(e, function(e) {
|
||||||
|
console.log('Toolbar');
|
||||||
|
if (!localStorage.formatText_toolbar || localStorage.formatText_toolbar == 'false') {
|
||||||
|
localStorage.formatText_toolbar = 'true';
|
||||||
|
if (window.Options && Options.get_tab('general')) e.target.checked = true;
|
||||||
|
formatText.build_toolbars();
|
||||||
|
} else {
|
||||||
|
localStorage.formatText_toolbar = 'false';
|
||||||
|
if (window.Options && Options.get_tab('general')) e.target.checked = false;
|
||||||
|
$('.format-text').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure the tab settings are switch properly at loadup
|
||||||
|
if (window.Options && Options.get_tab('general')) {
|
||||||
|
if (localStorage.formatText_keybinds == 'true') $(s1)[0].checked = true;
|
||||||
|
else $(s1)[0].checked = false;
|
||||||
|
if (localStorage.formatText_toolbar == 'true') $(s2)[0].checked = true;
|
||||||
|
else $(s2)[0].checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial toolbar injection
|
||||||
|
formatText.build_toolbars();
|
||||||
|
|
||||||
|
//attach listener to <body> so it also works on quick-reply box
|
||||||
|
$('body').on('keydown', '[name="body"]', function(e) {
|
||||||
|
if (!localStorage.formatText_keybinds || localStorage.formatText_keybinds == 'false') return;
|
||||||
|
var key = String.fromCharCode(e.which).toLowerCase();
|
||||||
|
var rules = JSON.parse(localStorage.formatText_rules);
|
||||||
|
for (var index in rules) {
|
||||||
|
if (!rules.hasOwnProperty(index)) continue;
|
||||||
|
if (key === rules[index].key && e.ctrlKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.shiftKey) {
|
||||||
|
formatText.wrap(e.target, 'textarea[name="body"]', index, true);
|
||||||
|
} else {
|
||||||
|
formatText.wrap(e.target, 'textarea[name="body"]', index, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signal that comment-toolbar loading has completed.
|
||||||
|
$(document).trigger('formatText');
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
})(jQuery);
|
||||||
|
}
|
|
@ -23,7 +23,15 @@ onready(function(){
|
||||||
.text(_('Expand all images'))
|
.text(_('Expand all images'))
|
||||||
.click(function() {
|
.click(function() {
|
||||||
$('a img.post-image').each(function() {
|
$('a img.post-image').each(function() {
|
||||||
if (!$(this).parent()[0].dataset.expanded)
|
// Don't expand YouTube embeds
|
||||||
|
if ($(this).parent().parent().hasClass('video-container'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// or WEBM
|
||||||
|
if (/^\/player\.php\?/.test($(this).parent().attr('href')))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!$(this).parent().data('expanded'))
|
||||||
$(this).parent().click();
|
$(this).parent().click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,8 +42,8 @@ onready(function(){
|
||||||
$('div#shrink-all-images a')
|
$('div#shrink-all-images a')
|
||||||
.text(_('Shrink all images'))
|
.text(_('Shrink all images'))
|
||||||
.click(function(){
|
.click(function(){
|
||||||
$('a img.post-image').each(function() {
|
$('a img.full-image').each(function() {
|
||||||
if ($(this).parent()[0].dataset.expanded)
|
if ($(this).parent().data('expanded'))
|
||||||
$(this).parent().click();
|
$(this).parent().click();
|
||||||
});
|
});
|
||||||
$(this).parent().remove();
|
$(this).parent().remove();
|
||||||
|
|
|
@ -17,7 +17,7 @@ $(function() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var url = $(this).attr('href');
|
var url = $(this).attr('href');
|
||||||
var body = $(this).parent().parent();
|
var body = $(this).parents('.body');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
|
|
|
@ -50,7 +50,7 @@ function add_favorites() {
|
||||||
$('.boardlist').append(boards);
|
$('.boardlist').append(boards);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (active_page == 'thread' || active_page == 'index') {
|
if (active_page == 'thread' || active_page == 'index' || active_page == 'catalog' || active_page == 'ukko') {
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
var favorites = JSON.parse(localStorage.favorites);
|
var favorites = JSON.parse(localStorage.favorites);
|
||||||
var is_board_favorite = ~$.inArray(board_name, favorites);
|
var is_board_favorite = ~$.inArray(board_name, favorites);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* file-selector.js - Add support for drag and drop file selection, and paste from clipbboard on supported browsers.
|
* file-selector.js - Add support for drag and drop file selection, and paste from clipboard on supported browsers.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
||||||
|
* $config['additional_javascript'][] = 'js/ajax.js';
|
||||||
* $config['additional_javascript'][] = 'js/file-selector.js';
|
* $config['additional_javascript'][] = 'js/file-selector.js';
|
||||||
*/
|
*/
|
||||||
function init_file_selector(max_images) {
|
function init_file_selector(max_images) {
|
||||||
|
|
|
@ -1,26 +1,69 @@
|
||||||
/*
|
/*
|
||||||
* fix-report-delete-submit.js
|
* fix-report-delete-submit.js
|
||||||
* https://github.com/savetheinternet/Tinyboard/blob/master/js/fix-report-delete-submit.js
|
|
||||||
*
|
|
||||||
* Fixes a known bug regarding the delete/report submit buttons.
|
|
||||||
*
|
|
||||||
* Released under the MIT license
|
|
||||||
* Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org>
|
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
||||||
|
* $config['additional_javascript'][] = 'js/post-menu.js';
|
||||||
* $config['additional_javascript'][] = 'js/fix-report-delete-submit.js';
|
* $config['additional_javascript'][] = 'js/fix-report-delete-submit.js';
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$(document).ready(function(){
|
if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko') {
|
||||||
$('form[name="postcontrols"] div.delete input:not([type="checkbox"]):not([type="submit"]):not([type="hidden"])').keypress(function(e) {
|
$(document).on('menu_ready', function(){
|
||||||
if(e.which == 13) {
|
var Menu = window.Menu;
|
||||||
|
|
||||||
|
if ($('#delete-fields #password').length) {
|
||||||
|
Menu.add_item("delete_post_menu", _("Delete post"));
|
||||||
|
Menu.add_item("delete_file_menu", _("Delete file"));
|
||||||
|
Menu.onclick(function(e, $buf) {
|
||||||
|
var ele = e.target.parentElement.parentElement;
|
||||||
|
var $ele = $(ele);
|
||||||
|
var threadId = $ele.parent().attr('id').replace('thread_', '');
|
||||||
|
var postId = $ele.find('.post_no').not('[id]').text();
|
||||||
|
var board_name = $ele.parent().data('board');
|
||||||
|
|
||||||
|
$buf.find('#delete_post_menu,#delete_file_menu').click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$(this).next().click();
|
$('#delete_'+postId).prop('checked', 'checked');
|
||||||
return false;
|
|
||||||
|
if ($(this).attr('id') === 'delete_file_menu') {
|
||||||
|
$('#delete_file').prop('checked', 'checked');
|
||||||
|
} else {
|
||||||
|
$('#delete_file').prop('checked', '');
|
||||||
}
|
}
|
||||||
return true;
|
$('input[type="hidden"][name="board"]').val(board_name);
|
||||||
|
$('input[name=delete][type=submit]').click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu.add_item("report_menu", _("Report"));
|
||||||
|
//Menu.add_item("global_report_menu", _("Global report"));
|
||||||
|
Menu.onclick(function(e, $buf) {
|
||||||
|
var ele = e.target.parentElement.parentElement;
|
||||||
|
var $ele = $(ele);
|
||||||
|
var threadId = $ele.parent().attr('id').replace('thread_', '');
|
||||||
|
var postId = $ele.find('.post_no').not('[id]').text();
|
||||||
|
var board_name = $ele.parent().data('board');
|
||||||
|
|
||||||
|
$buf.find('#report_menu,#global_report_menu').click(function(e) {
|
||||||
|
if ($(this).attr('id') === "global_report_menu") {
|
||||||
|
var global = '&global';
|
||||||
|
} else {
|
||||||
|
var global = '';
|
||||||
|
}
|
||||||
|
window.open(configRoot+'report.php?board='+board_name+'&post=delete_'+postId+global, "", (global?"width=600, height=575":"width=500, height=275"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('new_post', function(){
|
||||||
|
$('input.delete').hide();
|
||||||
|
});
|
||||||
|
$('input.delete').hide();
|
||||||
|
$('#post-moderation-fields').hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof window.Menu !== "undefined") {
|
||||||
|
$(document).trigger('menu_ready');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
if (active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || (window.Options && Options.get_tab('general')))
|
if (active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || (window.Options && Options.get_tab('general')))
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var force_anon = function() {
|
var force_anon = function() {
|
||||||
if($(this).children('a.capcode').length == 0) {
|
if ($(this).children('a.capcode').length == 0) {
|
||||||
var id = $(this).parent().children('a.post_no:eq(1)').text();
|
var id = $(this).parent().children('a.post_no:eq(1)').text();
|
||||||
|
|
||||||
if($(this).children('a.email').length != 0)
|
if ($(this).children('a.email').length != 0)
|
||||||
var p = $(this).children('a.email');
|
var p = $(this).children('a.email');
|
||||||
else
|
else
|
||||||
var p = $(this);
|
var p = $(this);
|
||||||
|
@ -29,7 +29,7 @@ $(document).ready(function() {
|
||||||
old_info[id] = {'name': p.children('span.name').text(), 'trip': p.children('span.trip').text()};
|
old_info[id] = {'name': p.children('span.name').text(), 'trip': p.children('span.trip').text()};
|
||||||
|
|
||||||
p.children('span.name').text('Anonymous');
|
p.children('span.name').text('Anonymous');
|
||||||
if(p.children('span.trip').length != 0)
|
if (p.children('span.trip').length != 0)
|
||||||
p.children('span.trip').text('');
|
p.children('span.trip').text('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -40,44 +40,60 @@ $(document).ready(function() {
|
||||||
|
|
||||||
var disable_fa = function() {
|
var disable_fa = function() {
|
||||||
$('p.intro label').each(function() {
|
$('p.intro label').each(function() {
|
||||||
if($(this).children('a.capcode').length == 0) {
|
if ($(this).children('a.capcode').length == 0) {
|
||||||
var id = $(this).parent().children('a.post_no:eq(1)').text();
|
var id = $(this).parent().children('a.post_no:eq(1)').text();
|
||||||
|
|
||||||
if(old_info[id]) {
|
if(old_info[id]) {
|
||||||
if($(this).children('a.email').length != 0)
|
if ($(this).children('a.email').length != 0)
|
||||||
var p = $(this).children('a.email');
|
var p = $(this).children('a.email');
|
||||||
else
|
else
|
||||||
var p = $(this);
|
var p = $(this);
|
||||||
|
|
||||||
p.children('span.name').text(old_info[id]['name']);
|
p.children('span.name').text(old_info[id]['name']);
|
||||||
if(p.children('span.trip').length != 0)
|
if (p.children('span.trip').length != 0)
|
||||||
p.children('span.trip').text(old_info[id]['trip']);
|
p.children('span.trip').text(old_info[id]['trip']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var toggle_id = function() {
|
||||||
|
if (localStorage.hideids == 'true'){
|
||||||
|
$(this).addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$(this).removeClass('hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
old_info = {};
|
old_info = {};
|
||||||
forced_anon = localStorage['forcedanon'] ? true : false;
|
forced_anon = localStorage['forcedanon'] ? true : false;
|
||||||
|
|
||||||
var selector, event;
|
|
||||||
if (window.Options && Options.get_tab('general')) {
|
if (window.Options && Options.get_tab('general')) {
|
||||||
selector = '#forced-anon';
|
var s1 = '#hide-ids', s2 = '#forced-anon', e = 'change';
|
||||||
event = 'change';
|
Options.extend_tab("general", "<label id='hide-ids'><input type='checkbox' /> "+_('Hide IDs')+"</label>");
|
||||||
Options.extend_tab("general", "<label id='forced-anon'><input type='checkbox' />"+_('Forced anonymity')+"</label>");
|
Options.extend_tab("general", "<label id='forced-anon'><input type='checkbox' /> "+_('Forced anonymity')+"</label>");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
selector = '#forced-anon';
|
var s1 = '#hide-ids', s2 = '#forced-anon', e = 'click';
|
||||||
event = 'click';
|
$('hr:first').before('<div id="hide-ids" style="text-align:right"><a class="unimportant" href="javascript:void(0)">Hide IDs</a></div>');
|
||||||
$('hr:first').before('<div id="forced-anon" style="text-align:right"><a class="unimportant" href="javascript:void(0)">-</a></div>');
|
$('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')) + ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(s1).on(e, function(e) {
|
||||||
|
if (!localStorage.hideids || localStorage.hideids == 'false') {
|
||||||
|
localStorage.hideids = 'true';
|
||||||
|
if (window.Options && Options.get_tab('general')) e.target.checked = true;
|
||||||
|
} else {
|
||||||
|
localStorage.hideids = 'false';
|
||||||
|
if (window.Options && Options.get_tab('general')) e.target.checked = false;
|
||||||
|
}
|
||||||
|
$('.poster_id').each(toggle_id);
|
||||||
|
});
|
||||||
|
|
||||||
$(selector).on(event, function() {
|
$(s2).on(e, function() {
|
||||||
forced_anon = !forced_anon;
|
forced_anon = !forced_anon;
|
||||||
|
if (forced_anon) {
|
||||||
if(forced_anon) {
|
|
||||||
$('div#forced-anon a').text(_('Forced anonymity')+' ('+_('enabled')+')');
|
$('div#forced-anon a').text(_('Forced anonymity')+' ('+_('enabled')+')');
|
||||||
localStorage.forcedanon = true;
|
localStorage.forcedanon = true;
|
||||||
enable_fa();
|
enable_fa();
|
||||||
|
@ -86,21 +102,27 @@ $(document).ready(function() {
|
||||||
delete localStorage.forcedanon;
|
delete localStorage.forcedanon;
|
||||||
disable_fa();
|
disable_fa();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// initial option setup on script load
|
||||||
|
if (localStorage.hideids == 'true'){
|
||||||
|
if (window.Options && Options.get_tab('general')) $('#hide-ids>input').prop('checked',true);
|
||||||
|
$('.poster_id').each(toggle_id);
|
||||||
|
}
|
||||||
|
|
||||||
if(forced_anon) {
|
if(forced_anon) {
|
||||||
enable_fa();
|
enable_fa();
|
||||||
|
|
||||||
if (window.Options && Options.get_tab('general')) {
|
if (window.Options && Options.get_tab('general')) {
|
||||||
$('#toggle-locked-threads>input').prop('checked', true);
|
$('#toggle-locked-threads>input').prop('checked', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on('new_post', function(e, post) {
|
$(document).on('new_post', function(e, post) {
|
||||||
if(forced_anon)
|
if (forced_anon)
|
||||||
$(post).find('p.intro label').each(force_anon);
|
$(post).find('p.intro label').each(force_anon);
|
||||||
|
if (localStorage.hideids == 'true')
|
||||||
|
$(post).find('.poster_id').each(toggle_id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ $(document).ready(function(){
|
||||||
|
|
||||||
$(this).hide().after(show_link);
|
$(this).hide().after(show_link);
|
||||||
|
|
||||||
if ($(img).parent()[0].dataset.expanded == 'true') {
|
if ($(img).parent().data('expanded') == 'true') {
|
||||||
$(img).parent().click();
|
$(img).parent().click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ $(document).ready(function(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fields_to_hide = 'div.post,div.video-container,video,iframe,img:not(.unanimated),canvas,p.fileinfo,a.hide-thread-link,div.new-posts,br';
|
var fields_to_hide = 'div.file,div.post,div.video-container,video,iframe,img:not(.unanimated),canvas,p.fileinfo,a.hide-thread-link,div.new-posts,br';
|
||||||
|
|
||||||
var do_hide_threads = function() {
|
var do_hide_threads = function() {
|
||||||
var id = $(this).children('p.intro').children('a.post_no:eq(1)').text();
|
var id = $(this).children('p.intro').children('a.post_no:eq(1)').text();
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
if (active_page == 'thread' || active_page == 'index') {
|
if (active_page == 'thread' || active_page == 'index') {
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
if (window.Options && Options.get_tab('general')) {
|
if (window.Options && Options.get_tab('general')) {
|
||||||
selector = '#color-ids>input';
|
var selector = '#color-ids>input';
|
||||||
event = 'change';
|
var e = 'change';
|
||||||
Options.extend_tab("general", "<label id='color-ids'><input type='checkbox' /> "+_('Color IDs')+"</label>");
|
Options.extend_tab("general", "<label id='color-ids'><input type='checkbox' /> "+_('Color IDs')+"</label>");
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
selector = '#color-ids';
|
var selector = '#color-ids';
|
||||||
event = 'click';
|
var e = 'click';
|
||||||
$('hr:first').before('<div id="color-ids" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+_('Color IDs')+'</a></div>')
|
$('hr:first').before('<div id="color-ids" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+_('Color IDs')+'</a></div>')
|
||||||
}
|
}
|
||||||
|
|
||||||
$(selector).on(event, function() {
|
$(selector).on(e, function() {
|
||||||
if (localStorage.color_ids === 'true') {
|
if (localStorage.color_ids === 'true') {
|
||||||
localStorage.color_ids = 'false';
|
localStorage.color_ids = 'false';
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,12 +50,6 @@ if (active_page == 'thread' || active_page == 'index') {
|
||||||
"border-radius": "8px",
|
"border-radius": "8px",
|
||||||
"color": ft
|
"color": ft
|
||||||
});
|
});
|
||||||
|
|
||||||
$(el).mouseover(function() {
|
|
||||||
$(this).css('color', '#800000'); // how about a CSS :hover rule instead?
|
|
||||||
}).mouseout(function() {
|
|
||||||
$(this).css('color', ft);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".poster_id").each(function(k, v){
|
$(".poster_id").each(function(k, v){
|
||||||
|
|
|
@ -180,3 +180,4 @@ function imageHoverEnd() { //Pashe, WTFPL
|
||||||
initImageHover();
|
initImageHover();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,13 @@ var load_next_page = function() {
|
||||||
if (!href) return;
|
if (!href) return;
|
||||||
|
|
||||||
var boardheader = $('<h2>'+_('Page')+' '+next_page.html()+'</h2>');
|
var boardheader = $('<h2>'+_('Page')+' '+next_page.html()+'</h2>');
|
||||||
var loading_ind = $('<h2>'+_('Loading...')+'</h2>').insertBefore('form[name="postcontrols"]>.delete:first');
|
var loading_ind = $('<h2>'+_('Loading...')+'</h2>').insertBefore('#post-moderation-fields');
|
||||||
|
|
||||||
$.get(href, function(data) {
|
$.get(href, function(data) {
|
||||||
var doc = $(data);
|
var doc = $(data);
|
||||||
|
|
||||||
loading_ind.remove();
|
loading_ind.remove();
|
||||||
boardheader.insertBefore('form[name="postcontrols"]>.delete:first');
|
boardheader.insertBefore('#post-moderation-fields');
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
|
@ -62,13 +62,15 @@ var load_next_page = function() {
|
||||||
if ($('div#thread_' + checkout).length == 0) {
|
if ($('div#thread_' + checkout).length == 0) {
|
||||||
// Delay DOM insertion to lessen the lag.
|
// Delay DOM insertion to lessen the lag.
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$($this).insertBefore('form[name="postcontrols"]>.delete:first');
|
$($this).insertBefore('#post-moderation-fields');
|
||||||
$(document).trigger('new_post', $this);
|
$(document).trigger('new_post', $this);
|
||||||
$($this).hide().slideDown();
|
$($this).hide().slideDown();
|
||||||
}, 500*i);
|
}, 500*i);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
loading = false;
|
loading = false;
|
||||||
scrolltest();
|
scrolltest();
|
||||||
|
@ -76,7 +78,6 @@ var load_next_page = function() {
|
||||||
|
|
||||||
next_page.addClass('selected');
|
next_page.addClass('selected');
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var button = $("<a href='#all'>"+_("All")+" </a>").prependTo(".pages");
|
var button = $("<a href='#all'>"+_("All")+" </a>").prependTo(".pages");
|
||||||
|
|
|
@ -7,65 +7,201 @@
|
||||||
* Copyright (c) 2013-2014 Marcin Łabanowski <marcin@6irc.net>
|
* Copyright (c) 2013-2014 Marcin Łabanowski <marcin@6irc.net>
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* // $config['additional_javascript'][] = 'js/jquery.min.js';
|
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
||||||
* $config['additional_javascript'][] = 'js/inline-expanding.js';
|
* $config['additional_javascript'][] = 'js/inline-expanding.js';
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onready(function(){
|
$(document).ready(function(){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var DEFAULT_MAX = 5; // default maximum image loads
|
||||||
var inline_expand_post = function() {
|
var inline_expand_post = function() {
|
||||||
var link = this.getElementsByTagName('a');
|
var link = this.getElementsByTagName('a');
|
||||||
|
|
||||||
for (var i = 0; i < link.length; i++) {
|
var loadingQueue = (function () {
|
||||||
if (typeof link[i] == "object" && link[i].childNodes && typeof link[i].childNodes[0] !== 'undefined' && link[i].childNodes[0].src && link[i].childNodes[0].className.match(/post-image/) && !link[i].className.match(/file/)) {
|
var MAX_IMAGES = localStorage.inline_expand_max || DEFAULT_MAX; // maximum number of images to load concurrently, 0 to disable
|
||||||
link[i].childNodes[0].style.maxWidth = '98%';
|
var loading = 0; // number of images that is currently loading
|
||||||
link[i].onclick = function(e) {
|
var waiting = []; // waiting queue
|
||||||
if (this.childNodes[0].className == 'hidden')
|
|
||||||
return false;
|
|
||||||
if (e.which == 2 || e.metaKey)
|
|
||||||
return true;
|
|
||||||
if (!this.dataset.src) {
|
|
||||||
this.parentNode.removeAttribute('style');
|
|
||||||
this.dataset.expanded = 'true';
|
|
||||||
|
|
||||||
if (this.childNodes[0].tagName === 'CANVAS') {
|
var enqueue = function (ele) {
|
||||||
this.removeChild(this.childNodes[0]);
|
waiting.push(ele);
|
||||||
this.childNodes[0].style.display = 'block';
|
};
|
||||||
}
|
var dequeue = function () {
|
||||||
|
return waiting.shift();
|
||||||
this.dataset.src= this.childNodes[0].src;
|
};
|
||||||
this.dataset.width = this.childNodes[0].style.width;
|
var update = function() {
|
||||||
this.dataset.height = this.childNodes[0].style.height;
|
var ele;
|
||||||
|
while (loading < MAX_IMAGES || MAX_IMAGES === 0) {
|
||||||
|
ele = dequeue();
|
||||||
this.childNodes[0].src = this.href;
|
if (ele) {
|
||||||
this.childNodes[0].style.width = 'auto';
|
++loading;
|
||||||
this.childNodes[0].style.height = 'auto';
|
ele.deferred.resolve();
|
||||||
this.childNodes[0].style.opacity = '0.4';
|
|
||||||
this.childNodes[0].style.filter = 'alpha(opacity=40)';
|
|
||||||
this.childNodes[0].onload = function() {
|
|
||||||
this.style.opacity = '';
|
|
||||||
delete this.style.filter;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
remove: function (ele) {
|
||||||
|
var i = waiting.indexOf(ele);
|
||||||
|
if (i > -1) {
|
||||||
|
waiting.splice(i, 1);
|
||||||
|
}
|
||||||
|
if ($(ele).data('imageLoading') === 'true') {
|
||||||
|
$(ele).data('imageLoading', 'false');
|
||||||
|
clearTimeout(ele.timeout);
|
||||||
|
--loading;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
add: function (ele) {
|
||||||
|
ele.deferred = $.Deferred();
|
||||||
|
ele.deferred.done(function () {
|
||||||
|
var $loadstart = $.Deferred();
|
||||||
|
var thumb = ele.childNodes[0];
|
||||||
|
var img = ele.childNodes[1];
|
||||||
|
|
||||||
|
var onLoadStart = function (img) {
|
||||||
|
if (img.naturalWidth) {
|
||||||
|
$loadstart.resolve(img, thumb);
|
||||||
|
} else {
|
||||||
|
return (ele.timeout = setTimeout(onLoadStart, 30, img));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(img).one('load', function () {
|
||||||
|
$.when($loadstart).done(function () {
|
||||||
|
// once fully loaded, update the waiting queue
|
||||||
|
--loading;
|
||||||
|
$(ele).data('imageLoading', 'false');
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$loadstart.done(function (img, thumb) {
|
||||||
|
thumb.style.display = 'none';
|
||||||
|
img.style.display = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
img.setAttribute('src', ele.href);
|
||||||
|
$(ele).data('imageLoading', 'true');
|
||||||
|
ele.timeout = onLoadStart(img);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading < MAX_IMAGES || MAX_IMAGES === 0) {
|
||||||
|
++loading;
|
||||||
|
ele.deferred.resolve();
|
||||||
|
} else {
|
||||||
|
enqueue(ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
for (var i = 0; i < link.length; i++) {
|
||||||
|
if (typeof link[i] == "object" && link[i].childNodes && typeof link[i].childNodes[0] !== 'undefined' &&
|
||||||
|
link[i].childNodes[0].src && link[i].childNodes[0].className.match(/post-image/) && !link[i].className.match(/file/)) {
|
||||||
|
link[i].onclick = function(e) {
|
||||||
|
var img, post_body, still_open, canvas, scroll;
|
||||||
|
var thumb = this.childNodes[0];
|
||||||
|
var padding = 5;
|
||||||
|
var boardlist = $('.boardlist')[0];
|
||||||
|
|
||||||
|
|
||||||
|
if (thumb.className == 'hidden')
|
||||||
|
return false;
|
||||||
|
if (e.which == 2 || e.ctrlKey) // open in new tab
|
||||||
|
return true;
|
||||||
|
if (!$(this).data('expanded')) {
|
||||||
|
|
||||||
if (~this.parentNode.className.indexOf('multifile'))
|
if (~this.parentNode.className.indexOf('multifile'))
|
||||||
this.parentNode.style.width = (parseInt(this.dataset.width)+40)+'px';
|
$(this).data('width', this.parentNode.style.width);
|
||||||
this.childNodes[0].src = this.dataset.src;
|
|
||||||
this.childNodes[0].style.width = this.dataset.width;
|
this.parentNode.removeAttribute('style');
|
||||||
this.childNodes[0].style.height = this.dataset.height;
|
$(this).data('expanded', 'true');
|
||||||
delete this.dataset.expanded;
|
|
||||||
delete this.dataset.src;
|
if (thumb.tagName === 'CANVAS') {
|
||||||
delete this.childNodes[0].style.opacity;
|
canvas = thumb;
|
||||||
delete this.childNodes[0].style.filter;
|
thumb = thumb.nextSibling;
|
||||||
|
this.removeChild(canvas);
|
||||||
|
canvas.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
thumb.style.opacity = '0.4';
|
||||||
|
thumb.style.filter = 'alpha(opacity=40)';
|
||||||
|
|
||||||
|
img = document.createElement('img');
|
||||||
|
img.className = 'full-image';
|
||||||
|
img.style.display = 'none';
|
||||||
|
img.setAttribute('alt', 'Fullsized image');
|
||||||
|
this.appendChild(img);
|
||||||
|
|
||||||
|
loadingQueue.add(this);
|
||||||
|
} else {
|
||||||
|
loadingQueue.remove(this);
|
||||||
|
|
||||||
|
scroll = false;
|
||||||
|
|
||||||
|
// scroll to thumb if not triggered by 'shrink all image'
|
||||||
|
if (e.target.className == 'full-image') {
|
||||||
|
scroll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (~this.parentNode.className.indexOf('multifile'))
|
||||||
|
this.parentNode.style.width = $(this).data('width');
|
||||||
|
|
||||||
|
thumb.style.opacity = '';
|
||||||
|
thumb.style.display = '';
|
||||||
|
if (thumb.nextSibling) this.removeChild(thumb.nextSibling); //full image loaded or loading
|
||||||
|
$(this).removeData('expanded');
|
||||||
|
delete thumb.style.filter;
|
||||||
|
|
||||||
|
// do the scrolling after page reflow
|
||||||
|
if (scroll) {
|
||||||
|
post_body = $(thumb).parentsUntil('form > div').last();
|
||||||
|
|
||||||
|
// on multifile posts, determin how many other images are still expanded
|
||||||
|
still_open = post_body.find('.post-image').filter(function(){
|
||||||
|
return $(this).parent().data('expanded') == 'true';
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
// deal with differnt boards' menu styles
|
||||||
|
if ($(boardlist).css('position') == 'fixed')
|
||||||
|
padding += boardlist.getBoundingClientRect().height;
|
||||||
|
|
||||||
|
if (still_open > 0) {
|
||||||
|
if (thumb.getBoundingClientRect().top - padding < 0)
|
||||||
|
$(document).scrollTop($(thumb).parent().parent().offset().top - padding);
|
||||||
|
} else {
|
||||||
|
if (post_body[0].getBoundingClientRect().top - padding < 0)
|
||||||
|
$(document).scrollTop(post_body.offset().top - padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (localStorage.no_animated_gif === 'true' && typeof unanimate_gif === 'function') {
|
if (localStorage.no_animated_gif === 'true' && typeof unanimate_gif === 'function') {
|
||||||
unanimate_gif(this.childNodes[0]);
|
unanimate_gif(thumb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// setting up user option
|
||||||
|
if (window.Options && Options.get_tab('general')) {
|
||||||
|
Options.extend_tab('general', '<span id="inline-expand-max">'+ _('Number of simultaneous image downloads (0 to disable): ') +
|
||||||
|
'<input type="number" step="1" min="0" size="4"></span>');
|
||||||
|
$('#inline-expand-max input')
|
||||||
|
.css('width', '50px')
|
||||||
|
.val(localStorage.inline_expand_max || DEFAULT_MAX)
|
||||||
|
.on('change', function (e) {
|
||||||
|
// validation in case some fucktard tries to enter a negative floating point number
|
||||||
|
var n = parseInt(e.target.value);
|
||||||
|
var val = (n<0) ? 0 : n;
|
||||||
|
|
||||||
|
localStorage.inline_expand_max = val;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.jQuery) {
|
if (window.jQuery) {
|
||||||
|
|
|
@ -123,8 +123,8 @@ $(document).ready(function() {
|
||||||
$clone.insertAfter(link.node)
|
$clone.insertAfter(link.node)
|
||||||
}
|
}
|
||||||
|
|
||||||
App.options.add('useInlining', 'Enable inlining')
|
App.options.add('useInlining', _('Enable inlining'))
|
||||||
App.options.add('hidePost', 'Hide inlined backlinked posts')
|
App.options.add('hidePost', _('Hide inlined backlinked posts'))
|
||||||
|
|
||||||
$('head').append(
|
$('head').append(
|
||||||
'<style>' +
|
'<style>' +
|
||||||
|
@ -139,7 +139,7 @@ $(document).ready(function() {
|
||||||
|
|
||||||
if (App.options.get('useInlining')) {
|
if (App.options.get('useInlining')) {
|
||||||
var assign_inline = function() {
|
var assign_inline = function() {
|
||||||
$('.body a:not([rel]), .mentioned a')
|
$('.body a[href*="'+location.pathname+'"]').not('[rel]').not('.toolong > a').add('.mentioned a')
|
||||||
.attr('onclick', null)// XXX disable highlightReply
|
.attr('onclick', null)// XXX disable highlightReply
|
||||||
.off('click')
|
.off('click')
|
||||||
.click(inline)
|
.click(inline)
|
||||||
|
|
8
js/jquery.min.js
vendored
8
js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
106
js/jquery.mixitup.min.js
vendored
106
js/jquery.mixitup.min.js
vendored
|
@ -1,43 +1,67 @@
|
||||||
/*
|
/*
|
||||||
* MIXITUP - A CSS3 and JQuery Filter & Sort Plugin
|
Copyright 2014 KunkaLabs Limited.
|
||||||
* Version: 1.5.5
|
@author KunkaLabs Limited.
|
||||||
* License: Creative Commons Attribution-NoDerivs 3.0 Unported - CC BY-ND 3.0
|
@link https://mixitup.kunkalabs.com
|
||||||
* http://creativecommons.org/licenses/by-nd/3.0/
|
|
||||||
* This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder.
|
|
||||||
* Author: Patrick Kunka
|
|
||||||
* Copyright 2012-2013 Patrick Kunka, Barrel LLC, All Rights Reserved
|
|
||||||
*
|
|
||||||
* http://mixitup.io
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function(d){function r(e,c,l,b,a){function f(){n.unbind("webkitTransitionEnd transitionend otransitionend oTransitionEnd");c&&x(c,l,b,a);a.startOrder=[];a.newOrder=[];a.origSort=[];a.checkSort=[];v.removeStyle(a.prefix+"filter, filter, "+a.prefix+"transform, transform, opacity, display").css(a.clean).removeAttr("data-checksum");window.atob||v.css({display:"none",opacity:"0"});n.removeStyle(a.prefix+"transition, transition, "+a.prefix+"perspective, perspective, "+a.prefix+"perspective-origin, perspective-origin, "+
|
@license Commercial use requires a commercial license.
|
||||||
(a.resizeContainer?"height":""));"list"==a.layoutMode?(p.css({display:a.targetDisplayList,opacity:"1"}),a.origDisplay=a.targetDisplayList):(p.css({display:a.targetDisplayGrid,opacity:"1"}),a.origDisplay=a.targetDisplayGrid);a.origLayout=a.layoutMode;setTimeout(function(){v.removeStyle(a.prefix+"transition, transition");a.mixing=!1;if("function"==typeof a.onMixEnd){var b=a.onMixEnd.call(this,a);a=b?b:a}})}clearInterval(a.failsafe);a.mixing=!0;a.filter=e;if("function"==typeof a.onMixStart){var g=a.onMixStart.call(this,
|
https://mixitup.kunkalabs.com/licenses/
|
||||||
a);a=g?g:a}for(var k=a.transitionSpeed,g=0;2>g;g++){var h=0==g?h=a.prefix:"";a.transition[h+"transition"]="all "+k+"ms linear";a.transition[h+"transform"]=h+"translate3d(0,0,0)";a.perspective[h+"perspective"]=a.perspectiveDistance+"px";a.perspective[h+"perspective-origin"]=a.perspectiveOrigin}var w=a.targetSelector,v=b.find(w);v.each(function(){this.data={}});var n=v.parent();n.css(a.perspective);a.easingFallback="ease-in-out";"smooth"==a.easing&&(a.easing="cubic-bezier(0.25, 0.46, 0.45, 0.94)");
|
|
||||||
"snap"==a.easing&&(a.easing="cubic-bezier(0.77, 0, 0.175, 1)");"windback"==a.easing&&(a.easing="cubic-bezier(0.175, 0.885, 0.320, 1.275)",a.easingFallback="cubic-bezier(0.175, 0.885, 0.320, 1)");"windup"==a.easing&&(a.easing="cubic-bezier(0.6, -0.28, 0.735, 0.045)",a.easingFallback="cubic-bezier(0.6, 0.28, 0.735, 0.045)");g="list"==a.layoutMode&&null!=a.listEffects?a.listEffects:a.effects;Array.prototype.indexOf&&(a.fade=-1<g.indexOf("fade")?"0":"",a.scale=-1<g.indexOf("scale")?"scale(.01)":"",a.rotateZ=
|
Non-commercial use permitted under terms of CC-BY-NC license.
|
||||||
-1<g.indexOf("rotateZ")?"rotate(180deg)":"",a.rotateY=-1<g.indexOf("rotateY")?"rotateY(90deg)":"",a.rotateX=-1<g.indexOf("rotateX")?"rotateX(90deg)":"",a.blur=-1<g.indexOf("blur")?"blur(8px)":"",a.grayscale=-1<g.indexOf("grayscale")?"grayscale(100%)":"");var p=d(),s=d(),t=[],r=!1;"string"===typeof e?t=z(e):(r=!0,d.each(e,function(a){t[a]=z(this)}));"or"==a.filterLogic?(""==t[0]&&t.shift(),1>t.length?s=s.add(b.find(w+":visible")):v.each(function(){var a=d(this);if(r){var b=0;d.each(t,function(d){this.length?
|
http://creativecommons.org/licenses/by-nc/3.0/
|
||||||
a.is("."+this.join(", ."))&&b++:0<b&&b++});b==t.length?p=p.add(a):s=s.add(a)}else a.is("."+t.join(", ."))?p=p.add(a):s=s.add(a)})):(p=p.add(n.find(w+"."+t.join("."))),s=s.add(n.find(w+":not(."+t.join(".")+"):visible")));e=p.length;var u=d(),q=d(),m=d();s.each(function(){var a=d(this);"none"!=a.css("display")&&(u=u.add(a),m=m.add(a))});if(p.filter(":visible").length==e&&!u.length&&!c){if(a.origLayout==a.layoutMode)return f(),!1;if(1==p.length)return"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass),
|
*/
|
||||||
m.css("display",a.targetDisplayList)):(b.addClass(a.gridClass),b.removeClass(a.listClass),m.css("display",a.targetDisplayGrid)),f(),!1}a.origHeight=n.height();if(p.length){b.removeClass(a.failClass);p.each(function(){var a=d(this);"none"==a.css("display")?q=q.add(a):m=m.add(a)});if(a.origLayout!=a.layoutMode&&!1==a.animateGridList)return"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass),m.css("display",a.targetDisplayList)):(b.addClass(a.gridClass),b.removeClass(a.listClass),
|
(function(f,l){f.MixItUp=function(){this._execAction("_constructor",0);f.extend(this,{selectors:{target:".mix",filter:".filter",sort:".sort"},animation:{enable:!0,effects:"fade scale",duration:600,easing:"ease",perspectiveDistance:"3000",perspectiveOrigin:"50% 50%",queue:!0,queueLimit:1,animateChangeLayout:!1,animateResizeContainer:!0,animateResizeTargets:!1,staggerSequence:!1,reverseOut:!1},callbacks:{onMixLoad:!1,onMixStart:!1,onMixBusy:!1,onMixEnd:!1,onMixFail:!1,_user:!1},controls:{enable:!0,
|
||||||
m.css("display",a.targetDisplayGrid)),f(),!1;if(!window.atob)return f(),!1;v.css(a.clean);m.each(function(){this.data.origPos=d(this).offset()});"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass),q.css("display",a.targetDisplayList)):(b.addClass(a.gridClass),b.removeClass(a.listClass),q.css("display",a.targetDisplayGrid));q.each(function(){this.data.showInterPos=d(this).offset()});u.each(function(){this.data.hideInterPos=d(this).offset()});m.each(function(){this.data.preInterPos=
|
live:!1,toggleFilterButtons:!1,toggleLogic:"or",activeClass:"active"},layout:{display:"inline-block",containerClass:"",containerClassFail:"fail"},load:{filter:"all",sort:!1},_$body:null,_$container:null,_$targets:null,_$parent:null,_$sortButtons:null,_$filterButtons:null,_suckMode:!1,_mixing:!1,_sorting:!1,_clicking:!1,_loading:!0,_changingLayout:!1,_changingClass:!1,_changingDisplay:!1,_origOrder:[],_startOrder:[],_newOrder:[],_activeFilter:null,_toggleArray:[],_toggleString:"",_activeSort:"default:asc",
|
||||||
d(this).offset()});"list"==a.layoutMode?m.css("display",a.targetDisplayList):m.css("display",a.targetDisplayGrid);c&&x(c,l,b,a);if(c&&A(a.origSort,a.checkSort))return f(),!1;u.hide();q.each(function(a){this.data.finalPos=d(this).offset()});m.each(function(){this.data.finalPrePos=d(this).offset()});a.newHeight=n.height();c&&x("reset",null,b,a);q.hide();m.css("display",a.origDisplay);"block"==a.origDisplay?(b.addClass(a.listClass),q.css("display",a.targetDisplayList)):(b.removeClass(a.listClass),q.css("display",
|
_newSort:null,_startHeight:null,_newHeight:null,_incPadding:!0,_newDisplay:null,_newClass:null,_targetsBound:0,_targetsDone:0,_queue:[],_$show:f(),_$hide:f()});this._execAction("_constructor",1)};f.MixItUp.prototype={constructor:f.MixItUp,_instances:{},_handled:{_filter:{},_sort:{}},_bound:{_filter:{},_sort:{}},_actions:{},_filters:{},extend:function(a){for(var c in a)f.MixItUp.prototype[c]=a[c]},addAction:function(a,c,b,d){f.MixItUp.prototype._addHook("_actions",a,c,b,d)},addFilter:function(a,c,
|
||||||
a.targetDisplayGrid));a.resizeContainer&&n.css("height",a.origHeight+"px");e={};for(g=0;2>g;g++)h=0==g?h=a.prefix:"",e[h+"transform"]=a.scale+" "+a.rotateX+" "+a.rotateY+" "+a.rotateZ,e[h+"filter"]=a.blur+" "+a.grayscale;q.css(e);m.each(function(){var b=this.data,c=d(this);c.hasClass("mix_tohide")?(b.preTX=b.origPos.left-b.hideInterPos.left,b.preTY=b.origPos.top-b.hideInterPos.top):(b.preTX=b.origPos.left-b.preInterPos.left,b.preTY=b.origPos.top-b.preInterPos.top);for(var e={},k=0;2>k;k++){var h=
|
b,d){f.MixItUp.prototype._addHook("_filters",a,c,b,d)},_addHook:function(a,c,b,d,e){a=f.MixItUp.prototype[a];var g={};e=1===e||"post"===e?"post":"pre";g[c]={};g[c][e]={};g[c][e][b]=d;f.extend(!0,a,g)},_init:function(a,c){this._execAction("_init",0,arguments);c&&f.extend(!0,this,c);this._$body=f("body");this._domNode=a;this._$container=f(a);this._$container.addClass(this.layout.containerClass);this._id=a.id;this._platformDetect();this._brake=this._getPrefixedCSS("transition","none");this._refresh(!0);
|
||||||
0==k?h=a.prefix:"";e[h+"transform"]="translate("+b.preTX+"px,"+b.preTY+"px)"}c.css(e)});"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass)):(b.addClass(a.gridClass),b.removeClass(a.listClass));setTimeout(function(){if(a.resizeContainer){for(var b={},c=0;2>c;c++){var e=0==c?e=a.prefix:"";b[e+"transition"]="all "+k+"ms ease-in-out";b.height=a.newHeight+"px"}n.css(b)}u.css("opacity",a.fade);q.css("opacity",1);q.each(function(){var b=this.data;b.tX=b.finalPos.left-b.showInterPos.left;
|
this._$parent=this._$targets.parent().length?this._$targets.parent():this._$container;this.load.sort&&(this._newSort=this._parseSort(this.load.sort),this._activeSort=this._newSortString=this.load.sort,this._sort(),this._printSort());this._activeFilter="all"===this.load.filter?this.selectors.target:"none"===this.load.filter?"":this.load.filter;this.controls.enable&&this._bindHandlers();if(this.controls.toggleFilterButtons){this._buildToggleArray();for(var b=0;b<this._toggleArray.length;b++)this._updateControls({filter:this._toggleArray[b],
|
||||||
b.tY=b.finalPos.top-b.showInterPos.top;for(var c={},e=0;2>e;e++){var h=0==e?h=a.prefix:"";c[h+"transition-property"]=h+"transform, "+h+"filter, opacity";c[h+"transition-timing-function"]=a.easing+", linear, linear";c[h+"transition-duration"]=k+"ms";c[h+"transition-delay"]="0";c[h+"transform"]="translate("+b.tX+"px,"+b.tY+"px)";c[h+"filter"]="none"}d(this).css("-webkit-transition","all "+k+"ms "+a.easingFallback).css(c)});m.each(function(){var b=this.data;b.tX=0!=b.finalPrePos.left?b.finalPrePos.left-
|
sort:this._activeSort},!0)}else this.controls.enable&&this._updateControls({filter:this._activeFilter,sort:this._activeSort});this._filter();this._init=!0;this._$container.data("mixItUp",this);this._execAction("_init",1,arguments);this._buildState();this._$targets.css(this._brake);this._goMix(this.animation.enable)},_platformDetect:function(){var a=["Webkit","Moz","O","ms"],c=["webkit","moz"],b=window.navigator.appVersion.match(/Chrome\/(\d+)\./)||!1,d="undefined"!==typeof InstallTrigger,e=function(b){for(var c=
|
||||||
b.preInterPos.left:0;b.tY=0!=b.finalPrePos.left?b.finalPrePos.top-b.preInterPos.top:0;for(var c={},e=0;2>e;e++){var h=0==e?h=a.prefix:"";c[h+"transition"]="all "+k+"ms "+a.easing;c[h+"transform"]="translate("+b.tX+"px,"+b.tY+"px)"}d(this).css("-webkit-transition","all "+k+"ms "+a.easingFallback).css(c)});b={};for(c=0;2>c;c++)e=0==c?e=a.prefix:"",b[e+"transition"]="all "+k+"ms "+a.easing+", "+e+"filter "+k+"ms linear, opacity "+k+"ms linear",b[e+"transform"]=a.scale+" "+a.rotateX+" "+a.rotateY+" "+
|
0;c<a.length;c++)if(a[c]+"Transition"in b.style)return{prefix:"-"+a[c].toLowerCase()+"-",vendor:a[c]};return"transition"in b.style?"":!1}(this._domNode);this._execAction("_platformDetect",0);this._chrome=b?parseInt(b[1],10):!1;this._ff=d?parseInt(window.navigator.userAgent.match(/rv:([^)]+)\)/)[1]):!1;this._prefix=e.prefix;this._vendor=e.vendor;(this._suckMode=window.atob&&this._prefix?!1:!0)&&(this.animation.enable=!1);this._ff&&4>=this._ff&&(this.animation.enable=!1);for(b=0;b<c.length&&!window.requestAnimationFrame;b++)window.requestAnimationFrame=
|
||||||
a.rotateZ,b[e+"filter"]=a.blur+" "+a.grayscale,b.opacity=a.fade;u.css(b);n.bind("webkitTransitionEnd transitionend otransitionend oTransitionEnd",function(a){if(-1<a.originalEvent.propertyName.indexOf("transform")||-1<a.originalEvent.propertyName.indexOf("opacity"))-1<w.indexOf(".")?d(a.target).hasClass(w.replace(".",""))&&f():d(a.target).is(w)&&f()})},10);a.failsafe=setTimeout(function(){a.mixing&&f()},k+400)}else{a.resizeContainer&&n.css("height",a.origHeight+"px");if(!window.atob)return f(),!1;
|
window[c[b]+"RequestAnimationFrame"];"function"!==typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"===typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype});this._domNode.nextElementSibling===l&&Object.defineProperty(Element.prototype,"nextElementSibling",{get:function(){for(var a=this.nextSibling;a;){if(1===a.nodeType)return a;a=a.nextSibling}return null}});this._execAction("_platformDetect",1)},_refresh:function(a,c){this._execAction("_refresh",
|
||||||
u=s;setTimeout(function(){n.css(a.perspective);if(a.resizeContainer){for(var c={},e=0;2>e;e++){var d=0==e?d=a.prefix:"";c[d+"transition"]="height "+k+"ms ease-in-out";c.height=a.minHeight+"px"}n.css(c)}v.css(a.transition);if(s.length){c={};for(e=0;2>e;e++)d=0==e?d=a.prefix:"",c[d+"transform"]=a.scale+" "+a.rotateX+" "+a.rotateY+" "+a.rotateZ,c[d+"filter"]=a.blur+" "+a.grayscale,c.opacity=a.fade;u.css(c);n.bind("webkitTransitionEnd transitionend otransitionend oTransitionEnd",function(c){if(-1<c.originalEvent.propertyName.indexOf("transform")||
|
0,arguments);this._$targets=this._$container.find(this.selectors.target);for(var b=0;b<this._$targets.length;b++){var d=this._$targets[b];if(d.dataset===l||c){d.dataset={};for(var e=0;e<d.attributes.length;e++){var g=d.attributes[e],k=g.name,g=g.value;-1<k.indexOf("data-")&&(k=this._helpers._camelCase(k.substring(5,k.length)),d.dataset[k]=g)}}d.mixParent===l&&(d.mixParent=this._id)}if(this._$targets.length&&a||!this._origOrder.length&&this._$targets.length)for(this._origOrder=[],b=0;b<this._$targets.length;b++)d=
|
||||||
-1<c.originalEvent.propertyName.indexOf("opacity"))b.addClass(a.failClass),f()})}else a.mixing=!1},10)}}function x(e,c,l,b){function a(a,b){var c=isNaN(1*a.attr(e))?a.attr(e).toLowerCase():1*a.attr(e),d=isNaN(1*b.attr(e))?b.attr(e).toLowerCase():1*b.attr(e);return c<d?-1:c>d?1:0}function f(a){"asc"==c?k.prepend(a).prepend(" "):k.append(a).append(" ")}function g(a){a=a.slice();for(var b=a.length,c=b;c--;){var e=parseInt(Math.random()*b),d=a[c];a[c]=a[e];a[e]=d}return a}l.find(b.targetSelector).wrapAll('<div class="mix_sorter"/>');
|
this._$targets[b],this._origOrder.push(d);this._execAction("_refresh",1,arguments)},_bindHandlers:function(){var a=this,c=f.MixItUp.prototype._bound._filter,b=f.MixItUp.prototype._bound._sort;a._execAction("_bindHandlers",0);if(a.controls.live)a._$body.on("click.mixItUp."+a._id,a.selectors.sort,function(){a._processClick(f(this),"sort")}).on("click.mixItUp."+a._id,a.selectors.filter,function(){a._processClick(f(this),"filter")});else a._$sortButtons=f(a.selectors.sort),a._$filterButtons=f(a.selectors.filter),
|
||||||
var k=l.find(".mix_sorter");b.origSort.length||k.find(b.targetSelector+":visible").each(function(){d(this).wrap("<s/>");b.origSort.push(d(this).parent().html().replace(/\s+/g,""));d(this).unwrap()});k.empty();if("reset"==e)d.each(b.startOrder,function(){k.append(this).append(" ")});else if("default"==e)d.each(b.origOrder,function(){f(this)});else if("random"==e)b.newOrder.length||(b.newOrder=g(b.startOrder)),d.each(b.newOrder,function(){k.append(this).append(" ")});else if("custom"==e)d.each(c,function(){f(this)});
|
a._$sortButtons.on("click.mixItUp."+a._id,function(){a._processClick(f(this),"sort")}),a._$filterButtons.on("click.mixItUp."+a._id,function(){a._processClick(f(this),"filter")});c[a.selectors.filter]=c[a.selectors.filter]===l?1:c[a.selectors.filter]+1;b[a.selectors.sort]=b[a.selectors.sort]===l?1:b[a.selectors.sort]+1;a._execAction("_bindHandlers",1)},_processClick:function(a,c){var b=this,d=function(a,c,d){var e=f.MixItUp.prototype;e._handled["_"+c][b.selectors[c]]=e._handled["_"+c][b.selectors[c]]===
|
||||||
else{if("undefined"===typeof b.origOrder[0].attr(e))return console.log("No such attribute found. Terminating"),!1;b.newOrder.length||(d.each(b.origOrder,function(){b.newOrder.push(d(this))}),b.newOrder.sort(a));d.each(b.newOrder,function(){f(this)})}b.checkSort=[];k.find(b.targetSelector+":visible").each(function(a){var c=d(this);0==a&&c.attr("data-checksum","1");c.wrap("<s/>");b.checkSort.push(c.parent().html().replace(/\s+/g,""));c.unwrap()});l.find(b.targetSelector).unwrap()}function B(e){for(var c=
|
l?1:e._handled["_"+c][b.selectors[c]]+1;e._handled["_"+c][b.selectors[c]]===e._bound["_"+c][b.selectors[c]]&&(a[(d?"remove":"add")+"Class"](b.controls.activeClass),delete e._handled["_"+c][b.selectors[c]])};b._execAction("_processClick",0,arguments);if(!b._mixing||b.animation.queue&&b._queue.length<b.animation.queueLimit){b._clicking=!0;if("sort"===c){var e=a.attr("data-sort");if(!a.hasClass(b.controls.activeClass)||-1<e.indexOf("random"))f(b.selectors.sort).removeClass(b.controls.activeClass),d(a,
|
||||||
["Webkit","Moz","O","ms"],d=0;d<c.length;d++)if(c[d]+"Transition"in e.style)return c[d];return"transition"in e.style?"":!1}function A(e,c){if(e.length!=c.length)return!1;for(var d=0;d<c.length;d++)if(e[d].compare&&!e[d].compare(c[d])||e[d]!==c[d])return!1;return!0}function z(e){e=e.replace(/\s{2,}/g," ");var c=e.split(" ");d.each(c,function(d){"all"==this&&(c[d]="mix_all")});""==c[0]&&c.shift();return c}var y={init:function(e){return this.each(function(){var c=window.navigator.appVersion.match(/Chrome\/(\d+)\./),
|
c),b.sort(e)}if("filter"===c){var g=a.attr("data-filter"),e="or"===b.controls.toggleLogic?",":"";b.controls.toggleFilterButtons?(b._buildToggleArray(),a.hasClass(b.controls.activeClass)?(d(a,c,!0),d=b._toggleArray.indexOf(g),b._toggleArray.splice(d,1)):(d(a,c),b._toggleArray.push(g)),b._toggleArray=f.grep(b._toggleArray,function(a){return a}),b._toggleString=b._toggleArray.join(e),b.filter(b._toggleString)):a.hasClass(b.controls.activeClass)||(f(b.selectors.filter).removeClass(b.controls.activeClass),
|
||||||
c=c?parseInt(c[1],10):!1,l=function(a){a=document.getElementById(a);var b=a.parentElement,c=document.createElement("div"),d=document.createDocumentFragment();b.insertBefore(c,a);d.appendChild(a);b.replaceChild(a,c)};(c&&31==c||32==c)&&l(this.id);var b={targetSelector:".mix",filterSelector:".filter",sortSelector:".sort",buttonEvent:"click",effects:["fade","scale"],listEffects:null,easing:"smooth",layoutMode:"grid",targetDisplayGrid:"inline-block",targetDisplayList:"block",listClass:"",gridClass:"",
|
d(a,c),b.filter(g))}b._execAction("_processClick",1,arguments)}else"function"===typeof b.callbacks.onMixBusy&&b.callbacks.onMixBusy.call(b._domNode,b._state,b),b._execAction("_processClickBusy",1,arguments)},_buildToggleArray:function(){var a=this._activeFilter.replace(/\s/g,"");this._execAction("_buildToggleArray",0,arguments);if("or"===this.controls.toggleLogic)this._toggleArray=a.split(",");else{this._toggleArray=a.split(".");!this._toggleArray[0]&&this._toggleArray.shift();for(var a=0,c;c=this._toggleArray[a];a++)this._toggleArray[a]=
|
||||||
transitionSpeed:600,showOnLoad:"all",sortOnLoad:!1,multiFilter:!1,filterLogic:"or",resizeContainer:!0,minHeight:0,failClass:"fail",perspectiveDistance:"3000",perspectiveOrigin:"50% 50%",animateGridList:!0,onMixLoad:null,onMixStart:null,onMixEnd:null,container:null,origOrder:[],startOrder:[],newOrder:[],origSort:[],checkSort:[],filter:"",mixing:!1,origDisplay:"",origLayout:"",origHeight:0,newHeight:0,isTouch:!1,resetDelay:0,failsafe:null,prefix:"",easingFallback:"ease-in-out",transition:{},perspective:{},
|
"."+c}this._execAction("_buildToggleArray",1,arguments)},_updateControls:function(a,c){var b={filter:a.filter,sort:a.sort},d="filter",e=null;this._execAction("_updateControls",0,arguments);a.filter===l&&(b.filter=this._activeFilter);a.sort===l&&(b.sort=this._activeSort);b.filter===this.selectors.target&&(b.filter="all");for(var g=0;2>g;g++){if(e=this.controls.live?f(this.selectors[d]):this["_$"+d+"Buttons"]){var k="[data-"+d+'="'+b[d]+'"]';c&&"filter"===d&&"none"!==b.filter&&""!==b.filter?e.filter(k).addClass(this.controls.activeClass):
|
||||||
clean:{},fade:"1",scale:"",rotateX:"",rotateY:"",rotateZ:"",blur:"",grayscale:""};e&&d.extend(b,e);this.config=b;d.support.touch="ontouchend"in document;d.support.touch&&(b.isTouch=!0,b.resetDelay=350);b.container=d(this);var a=b.container;b.prefix=B(a[0]);b.prefix=b.prefix?"-"+b.prefix.toLowerCase()+"-":"";a.find(b.targetSelector).each(function(){b.origOrder.push(d(this))});if(b.sortOnLoad){var f;d.isArray(b.sortOnLoad)?(c=b.sortOnLoad[0],f=b.sortOnLoad[1],d(b.sortSelector+"[data-sort="+b.sortOnLoad[0]+
|
e.removeClass(this.controls.activeClass).filter(k).addClass(this.controls.activeClass)}d="sort"}this._execAction("_updateControls",1,arguments)},_filter:function(){this._execAction("_filter",0);for(var a=0;a<this._$targets.length;a++){var c=f(this._$targets[a]);c.is(this._activeFilter)?this._$show=this._$show.add(c):this._$hide=this._$hide.add(c)}this._execAction("_filter",1)},_sort:function(){var a=this,c=function(a){a=a.slice();for(var b=a.length,c=b;c--;){var f=parseInt(Math.random()*b),h=a[c];
|
||||||
"][data-order="+b.sortOnLoad[1]+"]").addClass("active")):(d(b.sortSelector+"[data-sort="+b.sortOnLoad+"]").addClass("active"),c=b.sortOnLoad,b.sortOnLoad="desc");x(c,f,a,b)}for(f=0;2>f;f++)c=0==f?c=b.prefix:"",b.transition[c+"transition"]="all "+b.transitionSpeed+"ms ease-in-out",b.perspective[c+"perspective"]=b.perspectiveDistance+"px",b.perspective[c+"perspective-origin"]=b.perspectiveOrigin;for(f=0;2>f;f++)c=0==f?c=b.prefix:"",b.clean[c+"transition"]="none";"list"==b.layoutMode?(a.addClass(b.listClass),
|
a[c]=a[f];a[f]=h}return a};a._execAction("_sort",0);a._startOrder=[];for(var b=0;b<a._$targets.length;b++)a._startOrder.push(a._$targets[b]);switch(a._newSort[0].sortBy){case "default":a._newOrder=a._origOrder;break;case "random":a._newOrder=c(a._startOrder);break;case "custom":a._newOrder=a._newSort[0].order;break;default:a._newOrder=a._startOrder.concat().sort(function(b,c){return a._compare(b,c)})}a._execAction("_sort",1)},_compare:function(a,c,b){b=b?b:0;var d=this,e=d._newSort[b].order,g=function(a){return a.dataset[d._newSort[b].sortBy]||
|
||||||
b.origDisplay=b.targetDisplayList):(a.addClass(b.gridClass),b.origDisplay=b.targetDisplayGrid);b.origLayout=b.layoutMode;f=b.showOnLoad.split(" ");d.each(f,function(){d(b.filterSelector+'[data-filter="'+this+'"]').addClass("active")});a.find(b.targetSelector).addClass("mix_all");"all"==f[0]&&(f[0]="mix_all",b.showOnLoad="mix_all");var g=d();d.each(f,function(){g=g.add(d("."+this))});g.each(function(){var a=d(this);"list"==b.layoutMode?a.css("display",b.targetDisplayList):a.css("display",b.targetDisplayGrid);
|
0},f=isNaN(1*g(a))?g(a).toLowerCase():1*g(a),g=isNaN(1*g(c))?g(c).toLowerCase():1*g(c);return f<g?"asc"===e?-1:1:f>g?"asc"===e?1:-1:f===g&&d._newSort.length>b+1?d._compare(a,c,b+1):0},_printSort:function(a){var c=a?this._startOrder:this._newOrder,b=this._$parent[0].querySelectorAll(this.selectors.target),d=b.length?b[b.length-1].nextElementSibling:null,e=document.createDocumentFragment();this._execAction("_printSort",0,arguments);for(var g=0;g<b.length;g++){var f=b[g],h=f.nextSibling;"absolute"!==
|
||||||
a.css(b.transition)});setTimeout(function(){b.mixing=!0;g.css("opacity","1");setTimeout(function(){"list"==b.layoutMode?g.removeStyle(b.prefix+"transition, transition").css({display:b.targetDisplayList,opacity:1}):g.removeStyle(b.prefix+"transition, transition").css({display:b.targetDisplayGrid,opacity:1});b.mixing=!1;if("function"==typeof b.onMixLoad){var a=b.onMixLoad.call(this,b);b=a?a:b}},b.transitionSpeed)},10);b.filter=b.showOnLoad;d(b.sortSelector).bind(b.buttonEvent,function(){if(!b.mixing){var c=
|
f.style.position&&(h&&"#text"===h.nodeName&&this._$parent[0].removeChild(h),this._$parent[0].removeChild(f))}for(g=0;g<c.length;g++)b=c[g],"default"!==this._newSort[0].sortBy||"desc"!==this._newSort[0].order||a?(e.appendChild(b),e.appendChild(document.createTextNode(" "))):(e.insertBefore(b,e.firstChild),e.insertBefore(document.createTextNode(" "),b));d?this._$parent[0].insertBefore(e,d):this._$parent[0].appendChild(e);this._execAction("_printSort",1,arguments)},_parseSort:function(a){for(var c="string"===
|
||||||
d(this),e=c.attr("data-sort"),f=c.attr("data-order");if(!c.hasClass("active"))d(b.sortSelector).removeClass("active"),c.addClass("active");else if("random"!=e)return!1;a.find(b.targetSelector).each(function(){b.startOrder.push(d(this))});r(b.filter,e,f,a,b)}});d(b.filterSelector).bind(b.buttonEvent,function(){if(!b.mixing){var c=d(this);if(!1==b.multiFilter)d(b.filterSelector).removeClass("active"),c.addClass("active"),b.filter=c.attr("data-filter"),d(b.filterSelector+'[data-filter="'+b.filter+'"]').addClass("active");
|
typeof a?a.split(" "):[a],b=[],d=0;d<c.length;d++){var e="string"===typeof a?c[d].split(":"):["custom",c[d]],e={sortBy:this._helpers._camelCase(e[0]),order:e[1]||"asc"};b.push(e);if("default"===e.sortBy||"random"===e.sortBy)break}return this._execFilter("_parseSort",b,arguments)},_parseEffects:function(){var a=this,c={opacity:"",transformIn:"",transformOut:"",filter:""},b=function(b,c,d){return-1<a.animation.effects.indexOf(b)?c&&(b=a.animation.effects.indexOf(b+"("),-1<b)?(b=a.animation.effects.substring(b),
|
||||||
else{var e=c.attr("data-filter");c.hasClass("active")?(c.removeClass("active"),b.filter=b.filter.replace(RegExp("(\\s|^)"+e),"")):(c.addClass("active"),b.filter=b.filter+" "+e)}r(b.filter,null,null,a,b)}})})},toGrid:function(){return this.each(function(){var e=this.config;"grid"!=e.layoutMode&&(e.layoutMode="grid",r(e.filter,null,null,d(this),e))})},toList:function(){return this.each(function(){var e=this.config;"list"!=e.layoutMode&&(e.layoutMode="list",r(e.filter,null,null,d(this),e))})},filter:function(e){return this.each(function(){var c=
|
{val:/\(([^)]+)\)/.exec(b)[1]}):!0:!1},d=function(a,d){for(var f=[["scale",".01"],["translateX","20px"],["translateY","20px"],["translateZ","20px"],["rotateX","90deg"],["rotateY","90deg"],["rotateZ","180deg"]],h=0;h<f.length;h++){var m=f[h][0],l=f[h][1],p=d&&"scale"!==m,q=c[a],n;b(m)?(n=m+"(",m=b(m,!0).val||l,p=p?"-"===m.charAt(0)?m.substr(1,m.length):"-"+m:m,n=n+p+") "):n="";c[a]=q+n}};c.opacity=b("fade")?b("fade",!0).val||"0":"1";d("transformIn");a.animation.reverseOut?d("transformOut",!0):c.transformOut=
|
||||||
this.config;c.mixing||(d(c.filterSelector).removeClass("active"),d(c.filterSelector+'[data-filter="'+e+'"]').addClass("active"),r(e,null,null,d(this),c))})},sort:function(e){return this.each(function(){var c=this.config,l=d(this);if(!c.mixing){d(c.sortSelector).removeClass("active");if(d.isArray(e)){var b=e[0],a=e[1];d(c.sortSelector+'[data-sort="'+e[0]+'"][data-order="'+e[1]+'"]').addClass("active")}else d(c.sortSelector+'[data-sort="'+e+'"]').addClass("active"),b=e,a="desc";l.find(c.targetSelector).each(function(){c.startOrder.push(d(this))});
|
c.transformIn;c.transition={};c.transition=a._getPrefixedCSS("transition","all "+a.animation.duration+"ms "+a.animation.easing+", opacity "+a.animation.duration+"ms linear");a.animation.stagger=b("stagger")?!0:!1;a.animation.staggerDuration=parseInt(b("stagger")?b("stagger",!0).val?b("stagger",!0).val:100:100);return a._execFilter("_parseEffects",c)},_buildState:function(a){var c={};this._execAction("_buildState",0);c={activeFilter:""===this._activeFilter?"none":this._activeFilter,activeSort:a&&this._newSortString?
|
||||||
r(c.filter,b,a,l,c)}})},multimix:function(e){return this.each(function(){var c=this.config,l=d(this);multiOut={filter:c.filter,sort:null,order:"desc",layoutMode:c.layoutMode};d.extend(multiOut,e);c.mixing||(d(c.filterSelector).add(c.sortSelector).removeClass("active"),d(c.filterSelector+'[data-filter="'+multiOut.filter+'"]').addClass("active"),"undefined"!==typeof multiOut.sort&&(d(c.sortSelector+'[data-sort="'+multiOut.sort+'"][data-order="'+multiOut.order+'"]').addClass("active"),l.find(c.targetSelector).each(function(){c.startOrder.push(d(this))})),
|
this._newSortString:this._activeSort,fail:!this._$show.length&&""!==this._activeFilter,$targets:this._$targets,$show:this._$show,$hide:this._$hide,totalTargets:this._$targets.length,totalShow:this._$show.length,totalHide:this._$hide.length,display:a&&this._newDisplay?this._newDisplay:this.layout.display};if(a)return this._execFilter("_buildState",c);this._state=c;this._execAction("_buildState",1)},_goMix:function(a){var c=this,b=function(){if(c._chrome&&31===c._chrome){var a=c._$parent[0],b=a.parentElement,
|
||||||
c.layoutMode=multiOut.layoutMode,r(multiOut.filter,multiOut.sort,multiOut.order,l,c))})},remix:function(e){return this.each(function(){var c=this.config,l=d(this);c.origOrder=[];l.find(c.targetSelector).each(function(){var b=d(this);b.addClass("mix_all");c.origOrder.push(b)});c.mixing||"undefined"===typeof e||(d(c.filterSelector).removeClass("active"),d(c.filterSelector+'[data-filter="'+e+'"]').addClass("active"),r(e,null,null,l,c))})}};d.fn.mixitup=function(d,c){if(y[d])return y[d].apply(this,Array.prototype.slice.call(arguments,
|
e=document.createElement("div"),f=document.createDocumentFragment();b.insertBefore(e,a);f.appendChild(a);b.replaceChild(a,e)}c._setInter();d()},d=function(){var a=window.pageYOffset,b=window.pageXOffset;c._getInterMixData();c._setFinal();c._getFinalMixData();window.pageYOffset!==a&&window.scrollTo(b,a);c._prepTargets();window.requestAnimationFrame?requestAnimationFrame(e):setTimeout(function(){e()},20)},e=function(){c._animateTargets();0===c._targetsBound&&c._cleanUp()},f=c._buildState(!0);c._execAction("_goMix",
|
||||||
1));if("object"===typeof d||!d)return y.init.apply(this,arguments)};d.fn.removeStyle=function(e){return this.each(function(){var c=d(this);e=e.replace(/\s+/g,"");var l=e.split(",");d.each(l,function(){var b=RegExp(this.toString()+"[^;]+;?","g");c.attr("style",function(a,c){if(c)return c.replace(b,"")})})})}})(jQuery);
|
0,arguments);!c.animation.duration&&(a=!1);c._mixing=!0;c._$container.removeClass(c.layout.containerClassFail);"function"===typeof c.callbacks.onMixStart&&c.callbacks.onMixStart.call(c._domNode,c._state,f,c);c._$container.trigger("mixStart",[c._state,f,c]);c._getOrigMixData();a&&!c._suckMode?window.requestAnimationFrame?requestAnimationFrame(b):b():c._cleanUp();c._execAction("_goMix",1,arguments)},_getTargetData:function(a,c){var b;a.dataset[c+"PosX"]=a.offsetLeft;a.dataset[c+"PosY"]=a.offsetTop;
|
||||||
|
this.animation.animateResizeTargets&&(b=window.getComputedStyle(a),a.dataset[c+"MarginBottom"]=parseInt(b.marginBottom),a.dataset[c+"MarginRight"]=parseInt(b.marginRight),a.dataset[c+"Width"]=a.offsetWidth,a.dataset[c+"Height"]=a.offsetHeight)},_getOrigMixData:function(){var a=this._suckMode?{boxSizing:""}:window.getComputedStyle(this._$parent[0]);this._incPadding="border-box"===(a.boxSizing||a[this._vendor+"BoxSizing"]);this._execAction("_getOrigMixData",0);!this._suckMode&&(this.effects=this._parseEffects());
|
||||||
|
this._$toHide=this._$hide.filter(":visible");this._$toShow=this._$show.filter(":hidden");this._$pre=this._$targets.filter(":visible");this._startHeight=this._incPadding?this._$parent.outerHeight():this._$parent.height();for(a=0;a<this._$pre.length;a++)this._getTargetData(this._$pre[a],"orig");this._execAction("_getOrigMixData",1)},_setInter:function(){this._execAction("_setInter",0);this._changingLayout&&this.animation.animateChangeLayout?(this._$toShow.css("display",this._newDisplay),this._changingClass&&
|
||||||
|
this._$container.removeClass(this.layout.containerClass).addClass(this._newClass)):this._$toShow.css("display",this.layout.display);this._execAction("_setInter",1)},_getInterMixData:function(){this._execAction("_getInterMixData",0);for(var a=0;a<this._$toShow.length;a++){var c=this._$toShow[a];this._getTargetData(c,"inter")}for(a=0;a<this._$pre.length;a++)c=this._$pre[a],this._getTargetData(c,"inter");this._execAction("_getInterMixData",1)},_setFinal:function(){this._execAction("_setFinal",0);this._sorting&&
|
||||||
|
this._printSort();this._$toHide.removeStyle("display");this._changingLayout&&this.animation.animateChangeLayout&&this._$pre.css("display",this._newDisplay);this._execAction("_setFinal",1)},_getFinalMixData:function(){this._execAction("_getFinalMixData",0);for(var a=0;a<this._$toShow.length;a++){var c=this._$toShow[a];this._getTargetData(c,"final")}for(a=0;a<this._$pre.length;a++)c=this._$pre[a],this._getTargetData(c,"final");this._newHeight=this._incPadding?this._$parent.outerHeight():this._$parent.height();
|
||||||
|
this._sorting&&this._printSort(!0);this._$toShow.removeStyle("display");this._$pre.css("display",this.layout.display);this._changingClass&&this.animation.animateChangeLayout&&this._$container.removeClass(this._newClass).addClass(this.layout.containerClass);this._execAction("_getFinalMixData",1)},_prepTargets:function(){var a={_in:this._getPrefixedCSS("transform",this.effects.transformIn),_out:this._getPrefixedCSS("transform",this.effects.transformOut)};this._execAction("_prepTargets",0);this.animation.animateResizeContainer&&
|
||||||
|
this._$parent.css("height",this._startHeight+"px");for(var c=0;c<this._$toShow.length;c++){var b=this._$toShow[c],d=f(b);b.style.opacity=this.effects.opacity;b.style.display=this._changingLayout&&this.animation.animateChangeLayout?this._newDisplay:this.layout.display;d.css(a._in);this.animation.animateResizeTargets&&(b.style.width=b.dataset.finalWidth+"px",b.style.height=b.dataset.finalHeight+"px",b.style.marginRight=-(b.dataset.finalWidth-b.dataset.interWidth)+1*b.dataset.finalMarginRight+"px",b.style.marginBottom=
|
||||||
|
-(b.dataset.finalHeight-b.dataset.interHeight)+1*b.dataset.finalMarginBottom+"px")}for(c=0;c<this._$pre.length;c++)b=this._$pre[c],d=f(b),a=this._getPrefixedCSS("transform","translate("+(b.dataset.origPosX-b.dataset.interPosX)+"px,"+(b.dataset.origPosY-b.dataset.interPosY)+"px)"),d.css(a),this.animation.animateResizeTargets&&(b.style.width=b.dataset.origWidth+"px",b.style.height=b.dataset.origHeight+"px",b.dataset.origWidth-b.dataset.finalWidth&&(b.style.marginRight=-(b.dataset.origWidth-b.dataset.interWidth)+
|
||||||
|
1*b.dataset.origMarginRight+"px"),b.dataset.origHeight-b.dataset.finalHeight&&(b.style.marginBottom=-(b.dataset.origHeight-b.dataset.interHeight)+1*b.dataset.origMarginBottom+"px"));this._execAction("_prepTargets",1)},_animateTargets:function(){var a,c;this._execAction("_animateTargets",0);this._targetsBound=this._targetsDone=0;this._$parent.css(this._getPrefixedCSS("perspective",this.animation.perspectiveDistance+"px")).css(this._getPrefixedCSS("perspective-origin",this.animation.perspectiveOrigin));
|
||||||
|
this.animation.animateResizeContainer&&this._$parent.css(this._getPrefixedCSS("transition","height "+this.animation.duration+"ms ease")).css("height",this._newHeight+"px");for(var b=0;b<this._$toShow.length;b++){var d=this._$toShow[b],e=f(d);a=d.dataset.finalPosX-d.dataset.interPosX;c=d.dataset.finalPosY-d.dataset.interPosY;var g=this._getDelay(b),k={};d.style.opacity="";for(d=0;2>d;d++){var h=0===d?h=this._prefix:"";this._ff&&20>=this._ff&&(k[h+"transition-property"]="all",k[h+"transition-timing-function"]=
|
||||||
|
this.animation.easing+"ms",k[h+"transition-duration"]=this.animation.duration+"ms");k[h+"transition-delay"]=g+"ms";k[h+"transform"]="translate("+a+"px,"+c+"px)"}(this.effects.transform||this.effects.opacity)&&this._bindTargetDone(e);this._ff&&20>=this._ff?e.css(k):e.css(this.effects.transition).css(k)}for(b=0;b<this._$pre.length;b++)d=this._$pre[b],e=f(d),a=d.dataset.finalPosX-d.dataset.interPosX,c=d.dataset.finalPosY-d.dataset.interPosY,g=this._getDelay(b),d.dataset.finalPosX===d.dataset.origPosX&&
|
||||||
|
d.dataset.finalPosY===d.dataset.origPosY||this._bindTargetDone(e),e.css(this._getPrefixedCSS("transition","all "+this.animation.duration+"ms "+this.animation.easing+" "+g+"ms")),e.css(this._getPrefixedCSS("transform","translate("+a+"px,"+c+"px)")),this.animation.animateResizeTargets&&(d.dataset.origWidth-d.dataset.finalWidth&&1*d.dataset.finalWidth&&(d.style.width=d.dataset.finalWidth+"px",d.style.marginRight=-(d.dataset.finalWidth-d.dataset.interWidth)+1*d.dataset.finalMarginRight+"px"),d.dataset.origHeight-
|
||||||
|
d.dataset.finalHeight&&1*d.dataset.finalHeight&&(d.style.height=d.dataset.finalHeight+"px",d.style.marginBottom=-(d.dataset.finalHeight-d.dataset.interHeight)+1*d.dataset.finalMarginBottom+"px"));this._changingClass&&this._$container.removeClass(this.layout.containerClass).addClass(this._newClass);for(b=0;b<this._$toHide.length;b++){d=this._$toHide[b];e=f(d);g=this._getDelay(b);a={};for(d=0;2>d;d++)h=0===d?h=this._prefix:"",a[h+"transition-delay"]=g+"ms",a[h+"transform"]=this.effects.transformOut,
|
||||||
|
a.opacity=this.effects.opacity;e.css(this.effects.transition).css(a);(this.effects.transform||this.effects.opacity)&&this._bindTargetDone(e)}this._execAction("_animateTargets",1)},_bindTargetDone:function(a){var c=this,b=a[0];c._execAction("_bindTargetDone",0,arguments);b.dataset.bound||(b.dataset.bound=!0,c._targetsBound++,a.on("webkitTransitionEnd.mixItUp transitionend.mixItUp",function(d){(-1<d.originalEvent.propertyName.indexOf("transform")||-1<d.originalEvent.propertyName.indexOf("opacity"))&&
|
||||||
|
f(d.originalEvent.target).is(c.selectors.target)&&(a.off(".mixItUp"),delete b.dataset.bound,c._targetDone())}));c._execAction("_bindTargetDone",1,arguments)},_targetDone:function(){this._execAction("_targetDone",0);this._targetsDone++;this._targetsDone===this._targetsBound&&this._cleanUp();this._execAction("_targetDone",1)},_cleanUp:function(){var a=this,c=a.animation.animateResizeTargets?"transform opacity width height margin-bottom margin-right":"transform opacity";unBrake=function(){a._$targets.removeStyle("transition",
|
||||||
|
a._prefix)};a._execAction("_cleanUp",0);a._changingLayout?a._$show.css("display",a._newDisplay):a._$show.css("display",a.layout.display);a._$targets.css(a._brake);a._$targets.removeStyle(c,a._prefix).removeAttr("data-inter-pos-x data-inter-pos-y data-final-pos-x data-final-pos-y data-orig-pos-x data-orig-pos-y data-orig-height data-orig-width data-final-height data-final-width data-inter-width data-inter-height data-orig-margin-right data-orig-margin-bottom data-inter-margin-right data-inter-margin-bottom data-final-margin-right data-final-margin-bottom");
|
||||||
|
a._$hide.removeStyle("display");a._$parent.removeStyle("height transition perspective-distance perspective perspective-origin-x perspective-origin-y perspective-origin perspectiveOrigin",a._prefix);a._sorting&&(a._printSort(),a._activeSort=a._newSortString,a._sorting=!1);a._changingLayout&&(a._changingDisplay&&(a.layout.display=a._newDisplay,a._changingDisplay=!1),a._changingClass&&(a._$parent.removeClass(a.layout.containerClass).addClass(a._newClass),a.layout.containerClass=a._newClass,a._changingClass=
|
||||||
|
!1),a._changingLayout=!1);a._refresh();a._buildState();a._state.fail&&a._$container.addClass(a.layout.containerClassFail);a._$show=f();a._$hide=f();window.requestAnimationFrame&&requestAnimationFrame(unBrake);a._mixing=!1;"function"===typeof a.callbacks._user&&a.callbacks._user.call(a._domNode,a._state,a);"function"===typeof a.callbacks.onMixEnd&&a.callbacks.onMixEnd.call(a._domNode,a._state,a);a._$container.trigger("mixEnd",[a._state,a]);a._state.fail&&("function"===typeof a.callbacks.onMixFail&&
|
||||||
|
a.callbacks.onMixFail.call(a._domNode,a._state,a),a._$container.trigger("mixFail",[a._state,a]));a._loading&&("function"===typeof a.callbacks.onMixLoad&&a.callbacks.onMixLoad.call(a._domNode,a._state,a),a._$container.trigger("mixLoad",[a._state,a]));a._queue.length&&(a._execAction("_queue",0),a.multiMix(a._queue[0][0],a._queue[0][1],a._queue[0][2]),a._queue.splice(0,1));a._execAction("_cleanUp",1);a._loading=!1},_getPrefixedCSS:function(a,c,b){var d={};for(i=0;2>i;i++){var e=0===i?this._prefix:"";
|
||||||
|
b?d[e+a]=e+c:d[e+a]=c}return this._execFilter("_getPrefixedCSS",d,arguments)},_getDelay:function(a){var c="function"===typeof this.animation.staggerSequence?this.animation.staggerSequence.call(this._domNode,a,this._state):a;return this._execFilter("_getDelay",this.animation.stagger?c*this.animation.staggerDuration:0,arguments)},_parseMultiMixArgs:function(a){for(var c={command:null,animate:this.animation.enable,callback:null},b=0;b<a.length;b++){var d=a[b];null!==d&&("object"===typeof d||"string"===
|
||||||
|
typeof d?c.command=d:"boolean"===typeof d?c.animate=d:"function"===typeof d&&(c.callback=d))}return this._execFilter("_parseMultiMixArgs",c,arguments)},_parseInsertArgs:function(a){for(var c={index:0,$object:f(),multiMix:{filter:this._state.activeFilter},callback:null},b=0;b<a.length;b++){var d=a[b];"number"===typeof d?c.index=d:"object"===typeof d&&d instanceof f?c.$object=d:"object"===typeof d&&this._helpers._isElement(d)?c.$object=f(d):"object"===typeof d&&null!==d?c.multiMix=d:"boolean"!==typeof d||
|
||||||
|
d?"function"===typeof d&&(c.callback=d):c.multiMix=!1}return this._execFilter("_parseInsertArgs",c,arguments)},_execAction:function(a,c,b){c=c?"post":"pre";if(!this._actions.isEmptyObject&&this._actions.hasOwnProperty(a))for(var d in this._actions[a][c])this._actions[a][c][d].call(this,b)},_execFilter:function(a,c,b){if(!this._filters.isEmptyObject&&this._filters.hasOwnProperty(a))for(var d in this._filters[a])return this._filters[a][d].call(this,b);else return c},_helpers:{_camelCase:function(a){return a.replace(/-([a-z])/g,
|
||||||
|
function(a){return a[1].toUpperCase()})},_isElement:function(a){return window.HTMLElement?a instanceof HTMLElement:null!==a&&1===a.nodeType&&"string"===a.nodeName}},isMixing:function(){return this._execFilter("isMixing",this._mixing)},filter:function(){var a=this._parseMultiMixArgs(arguments);this._clicking&&(this._toggleString="");this.multiMix({filter:a.command},a.animate,a.callback)},sort:function(){var a=this._parseMultiMixArgs(arguments);this.multiMix({sort:a.command},a.animate,a.callback)},
|
||||||
|
changeLayout:function(){var a=this._parseMultiMixArgs(arguments);this.multiMix({changeLayout:a.command},a.animate,a.callback)},multiMix:function(){var a=this._parseMultiMixArgs(arguments);this._execAction("multiMix",0,arguments);if(this._mixing)this.animation.queue&&this._queue.length<this.animation.queueLimit?(this._queue.push(arguments),this.controls.enable&&!this._clicking&&this._updateControls(a.command),this._execAction("multiMixQueue",1,arguments)):("function"===typeof this.callbacks.onMixBusy&&
|
||||||
|
this.callbacks.onMixBusy.call(this._domNode,this._state,this),this._$container.trigger("mixBusy",[this._state,this]),this._execAction("multiMixBusy",1,arguments));else{this.controls.enable&&!this._clicking&&(this.controls.toggleFilterButtons&&this._buildToggleArray(),this._updateControls(a.command,this.controls.toggleFilterButtons));2>this._queue.length&&(this._clicking=!1);delete this.callbacks._user;a.callback&&(this.callbacks._user=a.callback);var c=a.command.sort,b=a.command.filter,d=a.command.changeLayout;
|
||||||
|
this._refresh();c&&(this._newSort=this._parseSort(c),this._newSortString=c,this._sorting=!0,this._sort());b!==l&&(this._activeFilter=b="all"===b?this.selectors.target:b);this._filter();d&&(this._newDisplay="string"===typeof d?d:d.display||this.layout.display,this._newClass=d.containerClass||"",this._newDisplay!==this.layout.display||this._newClass!==this.layout.containerClass)&&(this._changingLayout=!0,this._changingClass=this._newClass!==this.layout.containerClass,this._changingDisplay=this._newDisplay!==
|
||||||
|
this.layout.display);this._$targets.css(this._brake);this._goMix(a.animate^this.animation.enable?a.animate:this.animation.enable);this._execAction("multiMix",1,arguments)}},insert:function(){var a=this._parseInsertArgs(arguments),c="function"===typeof a.callback?a.callback:null,b=document.createDocumentFragment(),d;this._refresh();d=this._$targets.length?a.index<this._$targets.length||!this._$targets.length?this._$targets[a.index]:this._$targets[this._$targets.length-1].nextElementSibling:this._$parent[0].children[0];
|
||||||
|
this._execAction("insert",0,arguments);if(a.$object){for(var e=0;e<a.$object.length;e++)b.appendChild(a.$object[e]),b.appendChild(document.createTextNode(" "));this._$parent[0].insertBefore(b,d)}this._execAction("insert",1,arguments);"object"===typeof a.multiMix&&this.multiMix(a.multiMix,c)},prepend:function(){var a=this._parseInsertArgs(arguments);this.insert(0,a.$object,a.multiMix,a.callback)},append:function(){var a=this._parseInsertArgs(arguments);this.insert(this._state.totalTargets,a.$object,
|
||||||
|
a.multiMix,a.callback)},getOption:function(a){var c=function(a,c){for(var e=c.split("."),f=e.pop(),k=e.length,h=1,m=e[0]||c;(a=a[m])&&h<k;)m=e[h],h++;if(a!==l)return a[f]!==l?a[f]:a};return a?this._execFilter("getOption",c(this,a),arguments):this},setOptions:function(a){this._execAction("setOptions",0,arguments);"object"===typeof a&&f.extend(!0,this,a);this._execAction("setOptions",1,arguments)},getState:function(){return this._execFilter("getState",this._state,this)},forceRefresh:function(){this._refresh(!1,
|
||||||
|
!0)},destroy:function(a){this._execAction("destroy",0,arguments);this._$body.add(f(this.selectors.sort)).add(f(this.selectors.filter)).off(".mixItUp");for(var c=0;c<this._$targets.length;c++){var b=this._$targets[c];a&&(b.style.display="");delete b.mixParent}this._execAction("destroy",1,arguments);delete f.MixItUp.prototype._instances[this._id]}};f.fn.mixItUp=function(){var a=arguments,c=[],b,d=function(a,b){var c=new f.MixItUp;c._execAction("_instantiate",0,arguments);a.id=a.id?a.id:"MixItUp"+("00000"+
|
||||||
|
(16777216*Math.random()<<0).toString(16)).substr(-6).toUpperCase();c._instances[a.id]||(c._instances[a.id]=c,c._init(a,b));c._execAction("_instantiate",1,arguments)};b=this.each(function(){if(a&&"string"===typeof a[0]){var b=f.MixItUp.prototype._instances[this.id];"isLoaded"===a[0]?c.push(b?!0:!1):(b=b[a[0]](a[1],a[2],a[3]),b!==l&&c.push(b))}else d(this,a[0])});return c.length?1<c.length?c:c[0]:b};f.fn.removeStyle=function(a,c){c=c?c:"";return this.each(function(){for(var b=a.split(" "),d=0;d<b.length;d++)for(var e=
|
||||||
|
0;4>e;e++){switch(e){case 0:var g=b[d];break;case 1:g=f.MixItUp.prototype._helpers._camelCase(g);break;case 2:g=c+b[d];break;case 3:g=f.MixItUp.prototype._helpers._camelCase(c+b[d])}this.style[g]!==l&&"unknown"!==typeof this.style[g]&&0<this.style[g].length&&(this.style[g]="");if(!c&&1===e)break}this.attributes&&this.attributes.style&&this.attributes.style!==l&&""===this.attributes.style.value&&this.attributes.removeNamedItem("style")})}})(jQuery);
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onready(function(){
|
$(document).ready(function(){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
var iso8601 = function(s) {
|
var iso8601 = function(s) {
|
||||||
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
|
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
|
||||||
s = s.replace(/-/,"/").replace(/-/,"/");
|
s = s.replace(/-/,"/").replace(/-/,"/");
|
||||||
|
@ -36,28 +38,78 @@ onready(function(){
|
||||||
return strftime(window.post_date, t, datelocale);
|
return strftime(window.post_date, t, datelocale);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function timeDifference(current, previous) {
|
||||||
|
|
||||||
|
var msPerMinute = 60 * 1000;
|
||||||
|
var msPerHour = msPerMinute * 60;
|
||||||
|
var msPerDay = msPerHour * 24;
|
||||||
|
var msPerMonth = msPerDay * 30;
|
||||||
|
var msPerYear = msPerDay * 365;
|
||||||
|
|
||||||
|
var elapsed = current - previous;
|
||||||
|
|
||||||
|
if (elapsed < msPerMinute) {
|
||||||
|
return 'Just now';
|
||||||
|
} else if (elapsed < msPerHour) {
|
||||||
|
return Math.round(elapsed/msPerMinute) + (Math.round(elapsed/msPerMinute)<=1 ? ' minute ago':' minutes ago');
|
||||||
|
} else if (elapsed < msPerDay ) {
|
||||||
|
return Math.round(elapsed/msPerHour ) + (Math.round(elapsed/msPerHour)<=1 ? ' hour ago':' hours ago');
|
||||||
|
} else if (elapsed < msPerMonth) {
|
||||||
|
return Math.round(elapsed/msPerDay) + (Math.round(elapsed/msPerDay)<=1 ? ' day ago':' days ago');
|
||||||
|
} else if (elapsed < msPerYear) {
|
||||||
|
return Math.round(elapsed/msPerMonth) + (Math.round(elapsed/msPerMonth)<=1 ? ' month ago':' months ago');
|
||||||
|
} else {
|
||||||
|
return Math.round(elapsed/msPerYear ) + (Math.round(elapsed/msPerYear)<=1 ? ' year ago':' years ago');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var do_localtime = function(elem) {
|
var do_localtime = function(elem) {
|
||||||
var times = elem.getElementsByTagName('time');
|
var times = elem.getElementsByTagName('time');
|
||||||
|
var currentTime = Date.now();
|
||||||
|
|
||||||
for(var i = 0; i < times.length; i++) {
|
for(var i = 0; i < times.length; i++) {
|
||||||
if(typeof times[i].getAttribute('data-local') == 'undefined')
|
var t = times[i].getAttribute('datetime');
|
||||||
continue;
|
var postTime = new Date(t);
|
||||||
|
|
||||||
var t = iso8601(times[i].getAttribute('datetime'));
|
|
||||||
|
|
||||||
|
|
||||||
times[i].setAttribute('data-local', 'true');
|
times[i].setAttribute('data-local', 'true');
|
||||||
times[i].innerHTML = dateformat(t);
|
|
||||||
};
|
if (localStorage.show_relative_time === 'false') {
|
||||||
|
times[i].innerHTML = dateformat(iso8601(t));
|
||||||
|
times[i].setAttribute('title', timeDifference(currentTime, postTime.getTime()));
|
||||||
|
} else {
|
||||||
|
times[i].innerHTML = timeDifference(currentTime, postTime.getTime());
|
||||||
|
times[i].setAttribute('title', dateformat(iso8601(t)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (window.Options && Options.get_tab('general') && window.jQuery) {
|
||||||
|
var interval_id;
|
||||||
|
Options.extend_tab('general', '<label id="show-relative-time"><input type="checkbox">' + _('Show relative time') + '</label>');
|
||||||
|
|
||||||
|
$('#show-relative-time>input').on('change', function() {
|
||||||
|
if (localStorage.show_relative_time !== 'false') {
|
||||||
|
localStorage.show_relative_time = 'false';
|
||||||
|
clearInterval(interval_id);
|
||||||
|
} else {
|
||||||
|
localStorage.show_relative_time = 'true';
|
||||||
|
interval_id = setInterval(do_localtime, 30000, document);
|
||||||
|
}
|
||||||
|
// no need to refresh page
|
||||||
do_localtime(document);
|
do_localtime(document);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (localStorage.show_relative_time !== 'false') {
|
||||||
|
$('#show-relative-time>input').attr('checked','checked');
|
||||||
|
interval_id = setInterval(do_localtime, 30000, document);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.jQuery) {
|
|
||||||
// allow to work with auto-reload.js, etc.
|
// allow to work with auto-reload.js, etc.
|
||||||
$(document).on('new_post', function(e, post) {
|
$(document).on('new_post', function(e, post) {
|
||||||
do_localtime(post);
|
do_localtime(post);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
do_localtime(document);
|
||||||
|
});
|
||||||
|
|
|
@ -11,8 +11,9 @@
|
||||||
* //$config['additional_javascript'][] = 'js/options/general.js';
|
* //$config['additional_javascript'][] = 'js/options/general.js';
|
||||||
* $config['additional_javascript'][] = 'js/no-animated-gif.js';
|
* $config['additional_javascript'][] = 'js/no-animated-gif.js';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function unanimate_gif(e) {
|
function unanimate_gif(e) {
|
||||||
|
if ($(e).closest('.thread').children('.thread-hidden').length > 0) return;
|
||||||
|
|
||||||
if (active_page === "catalog")
|
if (active_page === "catalog")
|
||||||
var c = $('<canvas class="thread-image"></canvas>');
|
var c = $('<canvas class="thread-image"></canvas>');
|
||||||
else
|
else
|
||||||
|
@ -34,13 +35,19 @@ function unanimate_gif(e) {
|
||||||
$(e).addClass("unanimated").hide();
|
$(e).addClass("unanimated").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(function(){
|
||||||
|
|
||||||
|
var gif_finder = 'img.post-image[src$=".gif"], img.thread-image[src$=".gif"]';
|
||||||
|
|
||||||
function no_animated_gif() {
|
function no_animated_gif() {
|
||||||
var anim_gifs = $('img.post-image[src$=".gif"], img.thread-image[src$=".gif"]');
|
var anim_gifs = $(gif_finder);
|
||||||
localStorage.no_animated_gif = true;
|
localStorage.no_animated_gif = true;
|
||||||
$('#no-animated-gif>a').text(_('Animate GIFs'));
|
$('#no-animated-gif>a').text(_('Animate GIFs'));
|
||||||
$('#no-animated-gif>input').prop('checked', true);
|
$('#no-animated-gif>input').prop('checked', true);
|
||||||
|
|
||||||
$.each(anim_gifs, function(i, e) {unanimate_gif(e)} );
|
$.each(anim_gifs, function(i, e) {unanimate_gif(e)} );
|
||||||
|
|
||||||
|
$(document).on('new_post', new_post_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
function animated_gif() {
|
function animated_gif() {
|
||||||
|
@ -49,10 +56,17 @@ function animated_gif() {
|
||||||
localStorage.no_animated_gif = false;
|
localStorage.no_animated_gif = false;
|
||||||
$('#no-animated-gif>a').text(_('Unanimate GIFs'));
|
$('#no-animated-gif>a').text(_('Unanimate GIFs'));
|
||||||
$('#no-animated-gif>input').prop('checked', false);
|
$('#no-animated-gif>input').prop('checked', false);
|
||||||
|
|
||||||
|
$(document).off('new_post', new_post_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function new_post_handler(e, post) {
|
||||||
|
$(post).find(gif_finder).each(function(k, v) {
|
||||||
|
unanimate_gif(v);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko' || active_page == 'catalog') {
|
if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko' || active_page == 'catalog') {
|
||||||
$(function(){
|
|
||||||
var selector, event;
|
var selector, event;
|
||||||
if (window.Options && Options.get_tab('general')) {
|
if (window.Options && Options.get_tab('general')) {
|
||||||
selector = '#no-animated-gif>input';
|
selector = '#no-animated-gif>input';
|
||||||
|
@ -75,5 +89,6 @@ if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko' |
|
||||||
|
|
||||||
if (localStorage.no_animated_gif === 'true')
|
if (localStorage.no_animated_gif === 'true')
|
||||||
no_animated_gif();
|
no_animated_gif();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
72
js/options/fav.js
Normal file
72
js/options/fav.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
$(document).ready(function(){
|
||||||
|
//Creating functions
|
||||||
|
var generateList = function(){
|
||||||
|
var favStor = [];
|
||||||
|
for(var i=1; i<favorites.length+1; i++){
|
||||||
|
favStor.push($("#sortable > div:nth-child("+i+")").html());
|
||||||
|
}
|
||||||
|
return favStor;
|
||||||
|
} //This will generate a list of boards based off of the list on the screen
|
||||||
|
function removeBoard(boardNumber){
|
||||||
|
favorites.splice(boardNumber, 1);
|
||||||
|
localStorage.favorites = JSON.stringify(favorites);
|
||||||
|
$("#sortable > div:nth-child("+(boardNumber+1)+")").remove();
|
||||||
|
$("#minusList > div:nth-child("+(favorites.length+1)+")").remove();
|
||||||
|
add_favorites();
|
||||||
|
} //This removes a board from favorites, localStorage.favorites and the page
|
||||||
|
function addBoard(){
|
||||||
|
$("#sortable").append("<div>"+($("#plusBox").val())+"</div>");
|
||||||
|
$("#minusList").append( $('<div data-board="'+favorites.length+'" style="cursor: pointer; margin-right: 5px">-</div>').on('click', function(e){removeBoard($(this).data('board'));}) );
|
||||||
|
favorites.push($("#plusBox").val());
|
||||||
|
localStorage.favorites = JSON.stringify(favorites);
|
||||||
|
$("#plusBox").val(""); //Removing text from textbox
|
||||||
|
add_favorites();
|
||||||
|
} //This adds the text inside the textbox to favorites, localStorage.favorites and the page
|
||||||
|
|
||||||
|
var favorites = JSON.parse(localStorage.favorites);
|
||||||
|
Options.add_tab('fav-tab','star',_("Favorites"));
|
||||||
|
|
||||||
|
//Pregenerating list of boards
|
||||||
|
var favList = $('<div id="sortable" style="cursor: pointer; display: inline-block">');
|
||||||
|
for(var i=0; i<favorites.length; i++){
|
||||||
|
favList.append( $('<div>'+favorites[i]+'</div>') );
|
||||||
|
}
|
||||||
|
|
||||||
|
//Creating list of minus symbols to remove unwanted boards
|
||||||
|
var minusList = $('<div id="minusList" style="color: #0000FF; display: inline-block">');
|
||||||
|
for(var i=0; i<favorites.length; i++){
|
||||||
|
minusList.append( $('<div data-board="'+i+'" style="cursor: pointer; margin-right: 5px">-</div>').on('click', function(e){removeBoard($(this).data('board'));}) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//Help message so people understand how sorting boards works
|
||||||
|
$("<span>"+_("Drag the boards to sort them.")+"</span><br><br>").appendTo(Options.get_tab('fav-tab').content);
|
||||||
|
|
||||||
|
//Adding list of boards and minus symbols to remove boards with
|
||||||
|
$(minusList).appendTo(Options.get_tab('fav-tab').content); //Adding the list of minus symbols to the tab
|
||||||
|
$(favList).appendTo(Options.get_tab('fav-tab').content); //Adding the list of favorite boards to the tab
|
||||||
|
|
||||||
|
//Adding spacing and text box to right boards into
|
||||||
|
var addDiv = $("<div id='favs-add-board'>");
|
||||||
|
|
||||||
|
var plusBox = $("<input id=\"plusBox\" type=\"text\">").appendTo(addDiv);
|
||||||
|
plusBox.keydown(function( event ) {
|
||||||
|
if(event.keyCode == 13){
|
||||||
|
$("#plus").click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Adding plus symbol to use to add board
|
||||||
|
$("<div id=\"plus\">+</div>").css({
|
||||||
|
cursor: "pointer",
|
||||||
|
color: "#0000FF"
|
||||||
|
}).on('click', function(e){addBoard()}).appendTo(addDiv);
|
||||||
|
|
||||||
|
addDiv.appendTo(Options.get_tab('fav-tab').content); //Adding the plus button
|
||||||
|
|
||||||
|
favList.sortable(); //Making boards with sortable id use the sortable jquery function
|
||||||
|
favList.on('sortstop', function() {
|
||||||
|
favorites = generateList();
|
||||||
|
localStorage.favorites = JSON.stringify(favorites);
|
||||||
|
add_favorites();
|
||||||
|
});
|
||||||
|
});
|
|
@ -17,7 +17,7 @@ var textarea = $("<textarea></textarea>").css({
|
||||||
"font-size": 12,
|
"font-size": 12,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 35, bottom: 35,
|
top: 35, bottom: 35,
|
||||||
width: "calc(100% - 12px)", margin: 0, padding: 0, border: "1px solid black",
|
width: "calc(100% - 20px)", margin: 0, padding: "4px", border: "1px solid black",
|
||||||
left: 5, right: 5
|
left: 5, right: 5
|
||||||
}).appendTo(tab.content);
|
}).appendTo(tab.content);
|
||||||
var submit = $("<input type='button' value='"+_("Update custom CSS")+"'>").css({
|
var submit = $("<input type='button' value='"+_("Update custom CSS")+"'>").css({
|
||||||
|
|
|
@ -17,7 +17,7 @@ var textarea = $("<textarea></textarea>").css({
|
||||||
"font-size": 12,
|
"font-size": 12,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 35, bottom: 35,
|
top: 35, bottom: 35,
|
||||||
width: "calc(100% - 12px)", margin: 0, padding: 0, border: "1px solid black",
|
width: "calc(100% - 20px)", margin: 0, padding: "4px", border: "1px solid black",
|
||||||
left: 5, right: 5
|
left: 5, right: 5
|
||||||
}).appendTo(tab.content);
|
}).appendTo(tab.content);
|
||||||
var submit = $("<input type='button' value='"+_("Update custom Javascript")+"'>").css({
|
var submit = $("<input type='button' value='"+_("Update custom Javascript")+"'>").css({
|
||||||
|
|
852
js/post-filter.js
Normal file
852
js/post-filter.js
Normal file
|
@ -0,0 +1,852 @@
|
||||||
|
if (active_page === 'thread' || active_page === 'index' || active_page === 'catalog' || active_page === 'ukko') {
|
||||||
|
$(document).on('menu_ready', function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// returns blacklist object from storage
|
||||||
|
function getList() {
|
||||||
|
return JSON.parse(localStorage.postFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stores blacklist into storage and reruns the filter
|
||||||
|
function setList(blacklist) {
|
||||||
|
localStorage.postFilter = JSON.stringify(blacklist);
|
||||||
|
$(document).trigger('filter_page');
|
||||||
|
}
|
||||||
|
|
||||||
|
// unit: seconds
|
||||||
|
function timestamp() {
|
||||||
|
return Math.floor((new Date()).getTime() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initList(list, boardId, threadId) {
|
||||||
|
if (typeof list.postFilter[boardId] == 'undefined') {
|
||||||
|
list.postFilter[boardId] = {};
|
||||||
|
list.nextPurge[boardId] = {};
|
||||||
|
}
|
||||||
|
if (typeof list.postFilter[boardId][threadId] == 'undefined') {
|
||||||
|
list.postFilter[boardId][threadId] = [];
|
||||||
|
}
|
||||||
|
list.nextPurge[boardId][threadId] = {timestamp: timestamp(), interval: 86400}; // 86400 seconds == 1 day
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFilter(type, value, useRegex) {
|
||||||
|
var list = getList();
|
||||||
|
var filter = list.generalFilter;
|
||||||
|
var obj = {
|
||||||
|
type: type,
|
||||||
|
value: value,
|
||||||
|
regex: useRegex
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i=0; i<filter.length; i++) {
|
||||||
|
if (filter[i].type == type && filter[i].value == value && filter[i].regex == useRegex)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter.push(obj);
|
||||||
|
setList(list);
|
||||||
|
drawFilterList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFilter(type, value, useRegex) {
|
||||||
|
var list = getList();
|
||||||
|
var filter = list.generalFilter;
|
||||||
|
|
||||||
|
for (var i=0; i<filter.length; i++) {
|
||||||
|
if (filter[i].type == type && filter[i].value == value && filter[i].regex == useRegex) {
|
||||||
|
filter.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setList(list);
|
||||||
|
drawFilterList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nameSpanToString(el) {
|
||||||
|
var s = '';
|
||||||
|
|
||||||
|
$.each($(el).contents(), function(k,v) {
|
||||||
|
if (v.nodeName === 'IMG')
|
||||||
|
s=s+$(v).attr('alt')
|
||||||
|
|
||||||
|
if (v.nodeName === '#text')
|
||||||
|
s=s+v.nodeValue
|
||||||
|
});
|
||||||
|
return s.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
var blacklist = {
|
||||||
|
add: {
|
||||||
|
post: function (boardId, threadId, postId, hideReplies) {
|
||||||
|
var list = getList();
|
||||||
|
var filter = list.postFilter;
|
||||||
|
|
||||||
|
initList(list, boardId, threadId);
|
||||||
|
|
||||||
|
for (var i in filter[boardId][threadId]) {
|
||||||
|
if (filter[boardId][threadId][i].post == postId) return;
|
||||||
|
}
|
||||||
|
filter[boardId][threadId].push({
|
||||||
|
post: postId,
|
||||||
|
hideReplies: hideReplies
|
||||||
|
});
|
||||||
|
setList(list);
|
||||||
|
},
|
||||||
|
uid: function (boardId, threadId, uniqueId, hideReplies) {
|
||||||
|
var list = getList();
|
||||||
|
var filter = list.postFilter;
|
||||||
|
|
||||||
|
initList(list, boardId, threadId);
|
||||||
|
|
||||||
|
for (var i in filter[boardId][threadId]) {
|
||||||
|
if (filter[boardId][threadId][i].uid == uniqueId) return;
|
||||||
|
}
|
||||||
|
filter[boardId][threadId].push({
|
||||||
|
uid: uniqueId,
|
||||||
|
hideReplies: hideReplies
|
||||||
|
});
|
||||||
|
setList(list);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
post: function (boardId, threadId, postId) {
|
||||||
|
var list = getList();
|
||||||
|
var filter = list.postFilter;
|
||||||
|
|
||||||
|
// thread already pruned
|
||||||
|
if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined')
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (var i=0; i<filter[boardId][threadId].length; i++) {
|
||||||
|
if (filter[boardId][threadId][i].post == postId) {
|
||||||
|
filter[boardId][threadId].splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($.isEmptyObject(filter[boardId][threadId])) {
|
||||||
|
delete filter[boardId][threadId];
|
||||||
|
delete list.nextPurge[boardId][threadId];
|
||||||
|
|
||||||
|
if ($.isEmptyObject(filter[boardId])) {
|
||||||
|
delete filter[boardId];
|
||||||
|
delete list.nextPurge[boardId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setList(list);
|
||||||
|
},
|
||||||
|
uid: function (boardId, threadId, uniqueId) {
|
||||||
|
var list = getList();
|
||||||
|
var filter = list.postFilter;
|
||||||
|
|
||||||
|
// thread already pruned
|
||||||
|
if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined')
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (var i=0; i<filter[boardId][threadId].length; i++) {
|
||||||
|
if (filter[boardId][threadId][i].uid == uniqueId) {
|
||||||
|
filter[boardId][threadId].splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($.isEmptyObject(filter[boardId][threadId])) {
|
||||||
|
delete filter[boardId][threadId];
|
||||||
|
delete list.nextPurge[boardId][threadId];
|
||||||
|
|
||||||
|
if ($.isEmptyObject(filter[boardId])) {
|
||||||
|
delete filter[boardId];
|
||||||
|
delete list.nextPurge[boardId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setList(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hide/show the specified thread/post
|
||||||
|
*/
|
||||||
|
function hide(ele) {
|
||||||
|
var $ele = $(ele);
|
||||||
|
|
||||||
|
if ($(ele).data('hidden'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$(ele).data('hidden', true);
|
||||||
|
if ($ele.hasClass('op')) {
|
||||||
|
$ele.parent().find('.body, .files, .video-container').not($ele.children('.reply').children()).hide();
|
||||||
|
|
||||||
|
// hide thread replies on index view
|
||||||
|
if (active_page == 'index' || active_page == 'ukko') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').hide();
|
||||||
|
} else {
|
||||||
|
// normal posts
|
||||||
|
$ele.children('.body, .files, .video-container').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function show(ele) {
|
||||||
|
var $ele = $(ele);
|
||||||
|
|
||||||
|
$(ele).data('hidden', false);
|
||||||
|
if ($ele.hasClass('op')) {
|
||||||
|
$ele.parent().find('.body, .files, .video-container').show();
|
||||||
|
if (active_page == 'index') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').show();
|
||||||
|
} else {
|
||||||
|
// normal posts
|
||||||
|
$ele.children('.body, .files, .video-container').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create filter menu when the button is clicked
|
||||||
|
*/
|
||||||
|
function initPostMenu(pageData) {
|
||||||
|
var Menu = window.Menu;
|
||||||
|
var submenu;
|
||||||
|
Menu.add_item('filter-menu-hide', _('Hide post'));
|
||||||
|
Menu.add_item('filter-menu-unhide', _('Unhide post'));
|
||||||
|
|
||||||
|
submenu = Menu.add_submenu('filter-menu-add', _('Add filter'));
|
||||||
|
submenu.add_item('filter-add-post-plus', _('Post +'), _('Hide post and all replies'));
|
||||||
|
submenu.add_item('filter-add-id', _('ID'));
|
||||||
|
submenu.add_item('filter-add-id-plus', _('ID +'), _('Hide ID and all replies'));
|
||||||
|
submenu.add_item('filter-add-name', _('Name'));
|
||||||
|
submenu.add_item('filter-add-trip', _('Tripcode'));
|
||||||
|
|
||||||
|
submenu = Menu.add_submenu('filter-menu-remove', _('Remove filter'));
|
||||||
|
submenu.add_item('filter-remove-id', _('ID'));
|
||||||
|
submenu.add_item('filter-remove-name', _('Name'));
|
||||||
|
submenu.add_item('filter-remove-trip', _('Tripcode'));
|
||||||
|
|
||||||
|
Menu.onclick(function (e, $buffer) {
|
||||||
|
var ele = e.target.parentElement.parentElement;
|
||||||
|
var $ele = $(ele);
|
||||||
|
|
||||||
|
var threadId = $ele.parent().attr('id').replace('thread_', '');
|
||||||
|
var boardId = $ele.parent().data('board');
|
||||||
|
var postId = $ele.find('.post_no').not('[id]').text();
|
||||||
|
if (pageData.hasUID) {
|
||||||
|
var postUid = $ele.find('.poster_id').text();
|
||||||
|
}
|
||||||
|
|
||||||
|
var postName;
|
||||||
|
var postTrip = '';
|
||||||
|
if (!pageData.forcedAnon) {
|
||||||
|
postName = (typeof $ele.find('.name').contents()[0] == 'undefined') ? '' : nameSpanToString($ele.find('.name')[0]);
|
||||||
|
postTrip = $ele.find('.trip').text();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display logic and bind click handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// unhide button
|
||||||
|
if ($ele.data('hidden')) {
|
||||||
|
$buffer.find('#filter-menu-unhide').click(function () {
|
||||||
|
// if hidden due to post id, remove it from blacklist
|
||||||
|
// otherwise just show this post
|
||||||
|
blacklist.remove.post(boardId, threadId, postId);
|
||||||
|
show(ele);
|
||||||
|
});
|
||||||
|
$buffer.find('#filter-menu-hide').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$buffer.find('#filter-menu-unhide').addClass('hidden');
|
||||||
|
$buffer.find('#filter-menu-hide').click(function () {
|
||||||
|
blacklist.add.post(boardId, threadId, postId, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// post id
|
||||||
|
if (!$ele.data('hiddenByPost')) {
|
||||||
|
$buffer.find('#filter-add-post-plus').click(function () {
|
||||||
|
blacklist.add.post(boardId, threadId, postId, true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$buffer.find('#filter-add-post-plus').addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// UID
|
||||||
|
if (pageData.hasUID && !$ele.data('hiddenByUid')) {
|
||||||
|
$buffer.find('#filter-add-id').click(function () {
|
||||||
|
blacklist.add.uid(boardId, threadId, postUid, false);
|
||||||
|
});
|
||||||
|
$buffer.find('#filter-add-id-plus').click(function () {
|
||||||
|
blacklist.add.uid(boardId, threadId, postUid, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
$buffer.find('#filter-remove-id').addClass('hidden');
|
||||||
|
} else if (pageData.hasUID) {
|
||||||
|
$buffer.find('#filter-remove-id').click(function () {
|
||||||
|
blacklist.remove.uid(boardId, threadId, postUid);
|
||||||
|
});
|
||||||
|
|
||||||
|
$buffer.find('#filter-add-id').addClass('hidden');
|
||||||
|
$buffer.find('#filter-add-id-plus').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
// board doesn't use UID
|
||||||
|
$buffer.find('#filter-add-id').addClass('hidden');
|
||||||
|
$buffer.find('#filter-add-id-plus').addClass('hidden');
|
||||||
|
$buffer.find('#filter-remove-id').addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// name
|
||||||
|
if (!pageData.forcedAnon && !$ele.data('hiddenByName')) {
|
||||||
|
$buffer.find('#filter-add-name').click(function () {
|
||||||
|
addFilter('name', postName, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$buffer.find('#filter-remove-name').addClass('hidden');
|
||||||
|
} else if (!pageData.forcedAnon) {
|
||||||
|
$buffer.find('#filter-remove-name').click(function () {
|
||||||
|
removeFilter('name', postName, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$buffer.find('#filter-add-name').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
// board has forced anon
|
||||||
|
$buffer.find('#filter-remove-name').addClass('hidden');
|
||||||
|
$buffer.find('#filter-add-name').addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// tripcode
|
||||||
|
if (!pageData.forcedAnon && !$ele.data('hiddenByTrip') && postTrip !== '') {
|
||||||
|
$buffer.find('#filter-add-trip').click(function () {
|
||||||
|
addFilter('trip', postTrip, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$buffer.find('#filter-remove-trip').addClass('hidden');
|
||||||
|
} else if (!pageData.forcedAnon && postTrip !== '') {
|
||||||
|
$buffer.find('#filter-remove-trip').click(function () {
|
||||||
|
removeFilter('trip', postTrip, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$buffer.find('#filter-add-trip').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
// board has forced anon
|
||||||
|
$buffer.find('#filter-remove-trip').addClass('hidden');
|
||||||
|
$buffer.find('#filter-add-trip').addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide sub menus if all items are hidden
|
||||||
|
*/
|
||||||
|
if (!$buffer.find('#filter-menu-remove > ul').children().not('.hidden').length) {
|
||||||
|
$buffer.find('#filter-menu-remove').addClass('hidden');
|
||||||
|
}
|
||||||
|
if (!$buffer.find('#filter-menu-add > ul').children().not('.hidden').length) {
|
||||||
|
$buffer.find('#filter-menu-add').addClass('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hide/unhide thread on index view
|
||||||
|
*/
|
||||||
|
function quickToggle(ele, threadId, pageData) {
|
||||||
|
/*if ($(ele).find('.hide-thread-link').length)
|
||||||
|
$('.hide-thread-link').remove();*/
|
||||||
|
|
||||||
|
if ($(ele).hasClass('op') && !$(ele).find('.hide-thread-link').length) {
|
||||||
|
$('<a class="hide-thread-link" style="float:left;margin-right:5px" href="javascript:void(0)">[' + ($(ele).data('hidden') ? '+' : '–') + ']</a>')
|
||||||
|
.insertBefore($(ele).find(':not(h2,h2 *):first'))
|
||||||
|
.click(function() {
|
||||||
|
var postId = $(ele).find('.post_no').not('[id]').text();
|
||||||
|
var hidden = $(ele).data('hidden');
|
||||||
|
var boardId = $(ele).parents('.thread').data('board');
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
blacklist.remove.post(boardId, threadId, postId, false);
|
||||||
|
$(this).html('[–]');
|
||||||
|
} else {
|
||||||
|
blacklist.add.post(boardId, threadId, postId, false);
|
||||||
|
$(this).text('[+]');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* determine whether the reply post should be hidden
|
||||||
|
* - applies to all posts on page load or filtering rule change
|
||||||
|
* - apply to new posts on thread updates
|
||||||
|
* - must explicitly set the state of each attributes because filter will reapply to all posts after filtering rule change
|
||||||
|
*/
|
||||||
|
function filter(post, threadId, pageData) {
|
||||||
|
var $post = $(post);
|
||||||
|
|
||||||
|
var list = getList();
|
||||||
|
var postId = $post.find('.post_no').not('[id]').text();
|
||||||
|
var name, trip, uid, subject, comment;
|
||||||
|
var i, length, array, rule, pattern; // temp variables
|
||||||
|
|
||||||
|
var boardId = $post.data('board');
|
||||||
|
if (!boardId) boardId = $post.parents('.thread').data('board');
|
||||||
|
|
||||||
|
var localList = pageData.localList;
|
||||||
|
var noReplyList = pageData.noReplyList;
|
||||||
|
var hasUID = pageData.hasUID;
|
||||||
|
var forcedAnon = pageData.forcedAnon;
|
||||||
|
|
||||||
|
var hasTrip = ($post.find('.trip').length > 0);
|
||||||
|
var hasSub = ($post.find('.subject').length > 0);
|
||||||
|
|
||||||
|
$post.data('hidden', false);
|
||||||
|
$post.data('hiddenByUid', false);
|
||||||
|
$post.data('hiddenByPost', false);
|
||||||
|
$post.data('hiddenByName', false);
|
||||||
|
$post.data('hiddenByTrip', false);
|
||||||
|
$post.data('hiddenBySubject', false);
|
||||||
|
$post.data('hiddenByComment', false);
|
||||||
|
|
||||||
|
// add post with matched UID to localList
|
||||||
|
if (hasUID &&
|
||||||
|
typeof list.postFilter[boardId] != 'undefined' &&
|
||||||
|
typeof list.postFilter[boardId][threadId] != 'undefined') {
|
||||||
|
uid = $post.find('.poster_id').text();
|
||||||
|
array = list.postFilter[boardId][threadId];
|
||||||
|
|
||||||
|
for (i=0; i<array.length; i++) {
|
||||||
|
if (array[i].uid == uid) {
|
||||||
|
$post.data('hiddenByUid', true);
|
||||||
|
localList.push(postId);
|
||||||
|
if (array[i].hideReplies) noReplyList.push(postId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// match localList
|
||||||
|
if (localList.length) {
|
||||||
|
if ($.inArray(postId, localList) != -1) {
|
||||||
|
if ($post.data('hiddenByUid') !== true) $post.data('hiddenByPost', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches generalFilter
|
||||||
|
if (!forcedAnon)
|
||||||
|
name = (typeof $post.find('.name').contents()[0] == 'undefined') ? '' : nameSpanToString($post.find('.name')[0]);
|
||||||
|
if (!forcedAnon && hasTrip)
|
||||||
|
trip = $post.find('.trip').text();
|
||||||
|
if (hasSub)
|
||||||
|
subject = $post.find('.subject').text();
|
||||||
|
|
||||||
|
array = $post.find('.body').contents().filter(function () {if ($(this).text() !== '') return true;}).toArray();
|
||||||
|
array = $.map(array, function (ele) {
|
||||||
|
return $(ele).text().trim();
|
||||||
|
});
|
||||||
|
comment = array.join(' ');
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0, length = list.generalFilter.length; i < length; i++) {
|
||||||
|
rule = list.generalFilter[i];
|
||||||
|
|
||||||
|
if (rule.regex) {
|
||||||
|
pattern = new RegExp(rule.value);
|
||||||
|
switch (rule.type) {
|
||||||
|
case 'name':
|
||||||
|
if (!forcedAnon && pattern.test(name)) {
|
||||||
|
$post.data('hiddenByName', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'trip':
|
||||||
|
if (!forcedAnon && hasTrip && pattern.test(trip)) {
|
||||||
|
$post.data('hiddenByTrip', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'sub':
|
||||||
|
if (hasSub && pattern.test(subject)) {
|
||||||
|
$post.data('hiddenBySubject', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'com':
|
||||||
|
if (pattern.test(comment)) {
|
||||||
|
$post.data('hiddenByComment', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (rule.type) {
|
||||||
|
case 'name':
|
||||||
|
if (!forcedAnon && rule.value == name) {
|
||||||
|
$post.data('hiddenByName', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'trip':
|
||||||
|
if (!forcedAnon && hasTrip && rule.value == trip) {
|
||||||
|
$post.data('hiddenByTrip', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'sub':
|
||||||
|
pattern = new RegExp('\\b'+ rule.value+ '\\b');
|
||||||
|
if (hasSub && pattern.test(subject)) {
|
||||||
|
$post.data('hiddenBySubject', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'com':
|
||||||
|
pattern = new RegExp('\\b'+ rule.value+ '\\b');
|
||||||
|
if (pattern.test(comment)) {
|
||||||
|
$post.data('hiddenByComment', true);
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for link to filtered posts
|
||||||
|
$post.find('.body a').not('[rel="nofollow"]').each(function () {
|
||||||
|
var replyId = $(this).text().match(/^>>(\d+)$/);
|
||||||
|
|
||||||
|
if (!replyId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
replyId = replyId[1];
|
||||||
|
if ($.inArray(replyId, noReplyList) != -1) {
|
||||||
|
hide(post);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// post didn't match any filters
|
||||||
|
if (!$post.data('hidden')) {
|
||||||
|
show(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (re)runs the filter on the entire page
|
||||||
|
*/
|
||||||
|
function filterPage(pageData) {
|
||||||
|
var list = getList();
|
||||||
|
|
||||||
|
if (active_page != 'catalog') {
|
||||||
|
|
||||||
|
// empty the local and no-reply list
|
||||||
|
pageData.localList = [];
|
||||||
|
pageData.noReplyList = [];
|
||||||
|
|
||||||
|
$('.thread').each(function () {
|
||||||
|
var $thread = $(this);
|
||||||
|
// disregard the hidden threads constructed by post-hover.js
|
||||||
|
if ($thread.css('display') == 'none')
|
||||||
|
return;
|
||||||
|
|
||||||
|
var threadId = $thread.attr('id').replace('thread_', '');
|
||||||
|
var boardId = $thread.data('board');
|
||||||
|
var op = $thread.children('.op')[0];
|
||||||
|
var i, array; // temp variables
|
||||||
|
|
||||||
|
// add posts to localList and noReplyList
|
||||||
|
if (typeof list.postFilter[boardId] != 'undefined' && typeof list.postFilter[boardId][threadId] != 'undefined') {
|
||||||
|
array = list.postFilter[boardId][threadId];
|
||||||
|
for (i=0; i<array.length; i++) {
|
||||||
|
if ( typeof array[i].post == 'undefined')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pageData.localList.push(array[i].post);
|
||||||
|
if (array[i].hideReplies) pageData.noReplyList.push(array[i].post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// run filter on OP
|
||||||
|
filter(op, threadId, pageData);
|
||||||
|
quickToggle(op, threadId, pageData);
|
||||||
|
|
||||||
|
// iterate filter over each post
|
||||||
|
if (!$(op).data('hidden') || active_page == 'thread') {
|
||||||
|
$thread.find('.reply').not('.hidden').each(function () {
|
||||||
|
filter(this, threadId, pageData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var postFilter = list.postFilter[pageData.boardId];
|
||||||
|
var $collection = $('.mix');
|
||||||
|
|
||||||
|
if ($.isEmptyObject(postFilter))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// for each thread that has filtering rules
|
||||||
|
// check if filter contains thread OP and remove the thread from catalog
|
||||||
|
$.each(postFilter, function (key, thread) {
|
||||||
|
var threadId = key;
|
||||||
|
$.each(thread, function () {
|
||||||
|
if (this.post == threadId) {
|
||||||
|
$collection.filter('[data-id='+ threadId +']').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initStyle() {
|
||||||
|
var $ele, cssStyle, cssString;
|
||||||
|
|
||||||
|
$ele = $('<div>').addClass('post reply').hide().appendTo('body');
|
||||||
|
cssStyle = $ele.css(['background-color', 'border-color']);
|
||||||
|
cssStyle.hoverBg = $('body').css('background-color');
|
||||||
|
$ele.remove();
|
||||||
|
|
||||||
|
cssString = '\n/*** Generated by post-filter ***/\n' +
|
||||||
|
'#filter-control input[type=text] {width: 130px;}' +
|
||||||
|
'#filter-control input[type=checkbox] {vertical-align: middle;}' +
|
||||||
|
'#filter-control #clear {float: right;}\n' +
|
||||||
|
'#filter-container {margin-top: 20px; border: 1px solid; height: 270px; overflow: auto;}\n' +
|
||||||
|
'#filter-list {width: 100%; border-collapse: collapse;}\n' +
|
||||||
|
'#filter-list th {text-align: center; height: 20px; font-size: 14px; border-bottom: 1px solid;}\n' +
|
||||||
|
'#filter-list th:nth-child(1) {text-align: center; width: 70px;}\n' +
|
||||||
|
'#filter-list th:nth-child(2) {text-align: left;}\n' +
|
||||||
|
'#filter-list th:nth-child(3) {text-align: center; width: 58px;}\n' +
|
||||||
|
'#filter-list tr:not(#header) {height: 22px;}\n' +
|
||||||
|
'#filter-list tr:nth-child(even) {background-color:rgba(255, 255, 255, 0.5);}\n' +
|
||||||
|
'#filter-list td:nth-child(1) {text-align: center; width: 70px;}\n' +
|
||||||
|
'#filter-list td:nth-child(3) {text-align: center; width: 58px;}\n' +
|
||||||
|
'#confirm {text-align: right; margin-bottom: -18px; padding-top: 2px; font-size: 14px; color: #FF0000;}';
|
||||||
|
|
||||||
|
if (!$('style.generated-css').length) $('<style class="generated-css">').appendTo('head');
|
||||||
|
$('style.generated-css').html($('style.generated-css').html() + cssString);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawFilterList() {
|
||||||
|
var list = getList().generalFilter;
|
||||||
|
var $ele = $('#filter-list');
|
||||||
|
var $row, i, length, obj, val;
|
||||||
|
|
||||||
|
var typeName = {
|
||||||
|
name: 'name',
|
||||||
|
trip: 'tripcode',
|
||||||
|
sub: 'subject',
|
||||||
|
com: 'comment'
|
||||||
|
};
|
||||||
|
|
||||||
|
$ele.empty();
|
||||||
|
|
||||||
|
$ele.append('<tr id="header"><th>Type</th><th>Content</th><th>Remove</th></tr>');
|
||||||
|
for (i = 0, length = list.length; i < length; i++) {
|
||||||
|
obj = list[i];
|
||||||
|
|
||||||
|
// display formatting
|
||||||
|
val = (obj.regex) ? '/'+ obj.value +'/' : obj.value;
|
||||||
|
|
||||||
|
$row = $('<tr>');
|
||||||
|
$row.append(
|
||||||
|
'<td>'+ typeName[obj.type] +'</td>',
|
||||||
|
'<td>'+ val +'</td>',
|
||||||
|
$('<td>').append(
|
||||||
|
$('<a>').html('X')
|
||||||
|
.addClass('del-btn')
|
||||||
|
.attr('href', '#')
|
||||||
|
.data('type', obj.type)
|
||||||
|
.data('val', obj.value)
|
||||||
|
.data('useRegex', obj.regex)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$ele.append($row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initOptionsPanel() {
|
||||||
|
if (window.Options && !Options.get_tab('filter')) {
|
||||||
|
Options.add_tab('filter', 'list', _('Filters'));
|
||||||
|
Options.extend_tab('filter',
|
||||||
|
'<div id="filter-control">' +
|
||||||
|
'<select>' +
|
||||||
|
'<option value="name">'+_('Name')+'</option>' +
|
||||||
|
'<option value="trip">'+_('Tripcode')+'</option>' +
|
||||||
|
'<option value="sub">'+_('Subject')+'</option>' +
|
||||||
|
'<option value="com">'+_('Comment')+'</option>' +
|
||||||
|
'</select>' +
|
||||||
|
'<input type="text">' +
|
||||||
|
'<input type="checkbox">' +
|
||||||
|
'regex ' +
|
||||||
|
'<button id="set-filter">'+_('Add')+'</button>' +
|
||||||
|
'<button id="clear">'+_('Clear all filters')+'</button>' +
|
||||||
|
'<div id="confirm" class="hidden">' +
|
||||||
|
_('This will clear all filtering rules including hidden posts.')+' <a id="confirm-y" href="#">'+_('yes')+'</a> | <a id="confirm-n" href="#">'+_('no')+'</a>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div id="filter-container"><table id="filter-list"></table></div>'
|
||||||
|
);
|
||||||
|
drawFilterList();
|
||||||
|
|
||||||
|
// control buttons
|
||||||
|
$('#filter-control').on('click', '#set-filter', function () {
|
||||||
|
var type = $('#filter-control select option:selected').val();
|
||||||
|
var value = $('#filter-control input[type=text]').val();
|
||||||
|
var useRegex = $('#filter-control input[type=checkbox]').prop('checked');
|
||||||
|
|
||||||
|
//clear the input form
|
||||||
|
$('#filter-control input[type=text]').val('');
|
||||||
|
|
||||||
|
addFilter(type, value, useRegex);
|
||||||
|
drawFilterList();
|
||||||
|
});
|
||||||
|
$('#filter-control').on('click', '#clear', function () {
|
||||||
|
$('#filter-control #clear').addClass('hidden');
|
||||||
|
$('#filter-control #confirm').removeClass('hidden');
|
||||||
|
});
|
||||||
|
$('#filter-control').on('click', '#confirm-y', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$('#filter-control #clear').removeClass('hidden');
|
||||||
|
$('#filter-control #confirm').addClass('hidden');
|
||||||
|
setList({
|
||||||
|
generalFilter: [],
|
||||||
|
postFilter: {},
|
||||||
|
nextPurge: {},
|
||||||
|
lastPurge: timestamp()
|
||||||
|
});
|
||||||
|
drawFilterList();
|
||||||
|
});
|
||||||
|
$('#filter-control').on('click', '#confirm-n', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$('#filter-control #clear').removeClass('hidden');
|
||||||
|
$('#filter-control #confirm').addClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// remove button
|
||||||
|
$('#filter-list').on('click', '.del-btn', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var $ele = $(e.target);
|
||||||
|
var type = $ele.data('type');
|
||||||
|
var val = $ele.data('val');
|
||||||
|
var useRegex = $ele.data('useRegex');
|
||||||
|
|
||||||
|
removeFilter(type, val, useRegex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* clear out pruned threads
|
||||||
|
*/
|
||||||
|
function purge() {
|
||||||
|
var list = getList();
|
||||||
|
var board, thread, boardId, threadId;
|
||||||
|
var deferred;
|
||||||
|
var requestArray = [];
|
||||||
|
|
||||||
|
var successHandler = function (boardId, threadId) {
|
||||||
|
return function () {
|
||||||
|
// thread still alive, keep it in the list and increase the time between checks.
|
||||||
|
var list = getList();
|
||||||
|
var thread = list.nextPurge[boardId][threadId];
|
||||||
|
|
||||||
|
thread.timestamp = timestamp();
|
||||||
|
thread.interval = Math.floor(thread.interval * 1.5);
|
||||||
|
setList(list);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var errorHandler = function (boardId, threadId) {
|
||||||
|
return function (xhr) {
|
||||||
|
if (xhr.status == 404) {
|
||||||
|
var list = getList();
|
||||||
|
|
||||||
|
delete list.nextPurge[boardId][threadId];
|
||||||
|
delete list.postFilter[boardId][threadId];
|
||||||
|
if ($.isEmptyObject(list.nextPurge[boardId])) delete list.nextPurge[boardId];
|
||||||
|
if ($.isEmptyObject(list.postFilter[boardId])) delete list.postFilter[boardId];
|
||||||
|
setList(list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((timestamp() - list.lastPurge) < 86400) // less than 1 day
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (boardId in list.nextPurge) {
|
||||||
|
board = list.nextPurge[boardId];
|
||||||
|
for (threadId in board) {
|
||||||
|
thread = board[threadId];
|
||||||
|
if (timestamp() > (thread.timestamp + thread.interval)) {
|
||||||
|
// check if thread is pruned
|
||||||
|
deferred = $.ajax({
|
||||||
|
cache: false,
|
||||||
|
url: '/'+ boardId +'/res/'+ threadId +'.json',
|
||||||
|
success: successHandler(boardId, threadId),
|
||||||
|
error: errorHandler(boardId, threadId)
|
||||||
|
});
|
||||||
|
requestArray.push(deferred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when all requests complete
|
||||||
|
$.when.apply($, requestArray).always(function () {
|
||||||
|
var list = getList();
|
||||||
|
list.lastPurge = timestamp();
|
||||||
|
setList(list);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
if (typeof localStorage.postFilter === 'undefined') {
|
||||||
|
localStorage.postFilter = JSON.stringify({
|
||||||
|
generalFilter: [],
|
||||||
|
postFilter: {},
|
||||||
|
nextPurge: {},
|
||||||
|
lastPurge: timestamp()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageData = {
|
||||||
|
boardId: board_name, // get the id from the global variable
|
||||||
|
localList: [], // all the blacklisted post IDs or UIDs that apply to the current page
|
||||||
|
noReplyList: [], // any posts that replies to the contents of this list shall be hidden
|
||||||
|
hasUID: (document.getElementsByClassName('poster_id').length > 0),
|
||||||
|
forcedAnon: ($('th:contains(Name)').length === 0) // tests by looking for the Name label on the reply form
|
||||||
|
};
|
||||||
|
|
||||||
|
initStyle();
|
||||||
|
initOptionsPanel();
|
||||||
|
initPostMenu(pageData);
|
||||||
|
filterPage(pageData);
|
||||||
|
|
||||||
|
// on new posts
|
||||||
|
$(document).on('new_post', function (e, post) {
|
||||||
|
var threadId;
|
||||||
|
|
||||||
|
if ($(post).hasClass('reply')) {
|
||||||
|
threadId = $(post).parents('.thread').attr('id').replace('thread_', '');
|
||||||
|
} else {
|
||||||
|
threadId = $(post).attr('id').replace('thread_', '');
|
||||||
|
post = $(post).children('.op')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
filter(post, threadId, pageData);
|
||||||
|
quickToggle(post, threadId, pageData);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('filter_page', function () {
|
||||||
|
filterPage(pageData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// shift+click on catalog to hide thread
|
||||||
|
if (active_page == 'catalog') {
|
||||||
|
$(document).on('click', '.mix', function(e) {
|
||||||
|
if (e.shiftKey) {
|
||||||
|
var threadId = $(this).data('id').toString();
|
||||||
|
var postId = threadId;
|
||||||
|
blacklist.add.post(pageData.boardId, threadId, postId, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out the old threads
|
||||||
|
purge();
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof window.Menu !== "undefined") {
|
||||||
|
$(document).trigger('menu_ready');
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,10 +54,7 @@ onready(function(){
|
||||||
hovered_at = {'x': e.pageX, 'y': e.pageY};
|
hovered_at = {'x': e.pageX, 'y': e.pageY};
|
||||||
|
|
||||||
var start_hover = function($link) {
|
var start_hover = function($link) {
|
||||||
if($.contains($post[0], $link[0])) {
|
if ($post.is(':visible') &&
|
||||||
// link links to itself or to op; ignore
|
|
||||||
}
|
|
||||||
else if($post.is(':visible') &&
|
|
||||||
$post.offset().top >= $(window).scrollTop() &&
|
$post.offset().top >= $(window).scrollTop() &&
|
||||||
$post.offset().top + $post.height() <= $(window).scrollTop() + $(window).height()) {
|
$post.offset().top + $post.height() <= $(window).scrollTop() + $(window).height()) {
|
||||||
// post is in view
|
// post is in view
|
||||||
|
|
211
js/post-menu.js
Normal file
211
js/post-menu.js
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* post-menu.js - adds dropdown menu to posts
|
||||||
|
*
|
||||||
|
* Creates a global Menu object with four public methods:
|
||||||
|
*
|
||||||
|
* Menu.onclick(fnc)
|
||||||
|
* registers a function to be executed after button click, before the menu is displayed
|
||||||
|
* Menu.add_item(id, text[, title])
|
||||||
|
* adds an item to the top level of menu
|
||||||
|
* Menu.add_submenu(id, text)
|
||||||
|
* creates and returns a List object through which to manipulate the content of the submenu
|
||||||
|
* Menu.get_submenu(id)
|
||||||
|
* returns the submenu with the specified id from the top level menu
|
||||||
|
*
|
||||||
|
* The List object contains all the methods from Menu except onclick()
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* Menu.add_item('filter-menu-hide', 'Hide post');
|
||||||
|
* Menu.add_item('filter-menu-unhide', 'Unhide post');
|
||||||
|
*
|
||||||
|
* submenu = Menu.add_submenu('filter-menu-add', 'Add filter');
|
||||||
|
* submenu.add_item('filter-add-post-plus', 'Post +', 'Hide post and all replies');
|
||||||
|
* submenu.add_item('filter-add-id', 'ID');
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* $config['additional_javascript'][] = 'js/jquery.min.js';
|
||||||
|
* $config['additional_javascript'][] = 'js/post-menu.js';
|
||||||
|
*/
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
var List = function (menuId, text) {
|
||||||
|
this.id = menuId;
|
||||||
|
this.text = text;
|
||||||
|
this.items = [];
|
||||||
|
|
||||||
|
this.add_item = function (itemId, text, title) {
|
||||||
|
this.items.push(new Item(itemId, text, title));
|
||||||
|
};
|
||||||
|
this.list_items = function () {
|
||||||
|
var array = [];
|
||||||
|
var i, length, obj, $ele;
|
||||||
|
|
||||||
|
if ($.isEmptyObject(this.items))
|
||||||
|
return;
|
||||||
|
|
||||||
|
length = this.items.length;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
obj = this.items[i];
|
||||||
|
|
||||||
|
$ele = $('<li>', {id: obj.id}).text(obj.text);
|
||||||
|
if ('title' in obj) $ele.attr('title', obj.title);
|
||||||
|
|
||||||
|
if (obj instanceof Item) {
|
||||||
|
$ele.addClass('post-item');
|
||||||
|
} else {
|
||||||
|
$ele.addClass('post-submenu');
|
||||||
|
|
||||||
|
$ele.prepend(obj.list_items());
|
||||||
|
$ele.append($('<span>', {class: 'post-menu-arrow'}).text('»'));
|
||||||
|
}
|
||||||
|
|
||||||
|
array.push($ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $('<ul>').append(array);
|
||||||
|
};
|
||||||
|
this.add_submenu = function (menuId, text) {
|
||||||
|
var ele = new List(menuId, text);
|
||||||
|
this.items.push(ele);
|
||||||
|
return ele;
|
||||||
|
};
|
||||||
|
this.get_submenu = function (menuId) {
|
||||||
|
for (var i = 0; i < this.items.length; i++) {
|
||||||
|
if ((this.items[i] instanceof Item) || this.items[i].id != menuId) continue;
|
||||||
|
return this.items[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var Item = function (itemId, text, title) {
|
||||||
|
this.id = itemId;
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
// optional
|
||||||
|
if (typeof title != 'undefined') this.title = title;
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildMenu(e) {
|
||||||
|
var pos = $(e.target).offset();
|
||||||
|
var i, length;
|
||||||
|
|
||||||
|
var $menu = $('<div class="post-menu"></div>').append(mainMenu.list_items());
|
||||||
|
|
||||||
|
// execute registered click handlers
|
||||||
|
length = onclick_callbacks.length;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
onclick_callbacks[i](e, $menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set menu position and append to page
|
||||||
|
$menu.css({top: pos.top, left: pos.left + 20});
|
||||||
|
$('body').append($menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addButton(post) {
|
||||||
|
var $ele = $(post);
|
||||||
|
$ele.find('input.delete').after(
|
||||||
|
$('<a>', {href: '#', class: 'post-btn', title: 'Post menu'}).text('▶')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * * * *
|
||||||
|
Public methods
|
||||||
|
* * * * * * * * * */
|
||||||
|
var Menu = {};
|
||||||
|
var mainMenu = new List();
|
||||||
|
var onclick_callbacks = [];
|
||||||
|
|
||||||
|
Menu.onclick = function (fnc) {
|
||||||
|
onclick_callbacks.push(fnc);
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.add_item = function (itemId, text, title) {
|
||||||
|
mainMenu.add_item(itemId, text, title);
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.add_submenu = function (menuId, text) {
|
||||||
|
return mainMenu.add_submenu(menuId, text);
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.get_submenu = function (id) {
|
||||||
|
return mainMenu.get_submenu(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.Menu = Menu;
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * *
|
||||||
|
Initialize
|
||||||
|
* * * * * * * */
|
||||||
|
|
||||||
|
/* Styling
|
||||||
|
*/
|
||||||
|
var $ele, cssStyle, cssString;
|
||||||
|
|
||||||
|
$ele = $('<div>').addClass('post reply').hide().appendTo('body');
|
||||||
|
cssStyle = $ele.css(['border-top-color']);
|
||||||
|
cssStyle.hoverBg = $('body').css('background-color');
|
||||||
|
$ele.remove();
|
||||||
|
|
||||||
|
cssString =
|
||||||
|
'\n/*** Generated by post-menu ***/\n' +
|
||||||
|
'.post-menu {position: absolute; font-size: 12px; line-height: 1.3em;}\n' +
|
||||||
|
'.post-menu ul {\n' +
|
||||||
|
' background-color: '+ cssStyle['border-top-color'] +'; border: 1px solid #666;\n' +
|
||||||
|
' list-style: none; padding: 0; margin: 0; white-space: nowrap;\n}\n' +
|
||||||
|
'.post-menu .post-submenu{white-space: normal; width: 90px;}' +
|
||||||
|
'.post-menu .post-submenu>ul{white-space: nowrap; width: auto;}' +
|
||||||
|
'.post-menu li {cursor: pointer; position: relative; padding: 4px 4px; vertical-align: middle;}\n' +
|
||||||
|
'.post-menu li:hover {background-color: '+ cssStyle.hoverBg +';}\n' +
|
||||||
|
'.post-menu ul ul {display: none; position: absolute;}\n' +
|
||||||
|
'.post-menu li:hover>ul {display: block; left: 100%; margin-top: -3px;}\n' +
|
||||||
|
'.post-menu-arrow {float: right; margin-left: 10px;}\n' +
|
||||||
|
'.post-menu.hidden, .post-menu .hidden {display: none;}\n' +
|
||||||
|
'.post-btn {transition: transform 0.1s; width: 15px; text-align: center; font-size: 10pt; opacity: 0.8; text-decoration: none; margin: -6px 0px 0px -5px !important; display: inline-block;}\n' +
|
||||||
|
'.post-btn:hover {opacity: 1;}\n' +
|
||||||
|
'.post-btn-open {transform: rotate(90deg);}\n';
|
||||||
|
|
||||||
|
if (!$('style.generated-css').length) $('<style class="generated-css">').appendTo('head');
|
||||||
|
$('style.generated-css').html($('style.generated-css').html() + cssString);
|
||||||
|
|
||||||
|
/* Add buttons
|
||||||
|
*/
|
||||||
|
$('.reply:not(.hidden), .thread>.op').each(function () {
|
||||||
|
addButton(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* event handlers
|
||||||
|
*/
|
||||||
|
$('form[name=postcontrols]').on('click', '.post-btn', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var post = e.target.parentElement.parentElement;
|
||||||
|
$('.post-menu').remove();
|
||||||
|
|
||||||
|
if ($(e.target).hasClass('post-btn-open')) {
|
||||||
|
$('.post-btn-open').removeClass('post-btn-open');
|
||||||
|
} else {
|
||||||
|
// close previous button
|
||||||
|
$('.post-btn-open').removeClass('post-btn-open');
|
||||||
|
$(post).find('.post-btn').addClass('post-btn-open');
|
||||||
|
|
||||||
|
buildMenu(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', function (e){
|
||||||
|
if ($(e.target).hasClass('post-btn') || $(e.target).hasClass('post-submenu'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$('.post-menu').remove();
|
||||||
|
$('.post-btn-open').removeClass('post-btn-open');
|
||||||
|
});
|
||||||
|
|
||||||
|
// on new posts
|
||||||
|
$(document).on('new_post', function (e, post) {
|
||||||
|
addButton(post);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).trigger('menu_ready');
|
||||||
|
});
|
|
@ -281,12 +281,12 @@
|
||||||
|
|
||||||
$postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment'));
|
$postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment'));
|
||||||
|
|
||||||
$postForm.find('textarea:not([name="body"]),input[type="hidden"]').removeAttr('id').appendTo($dummyStuff);
|
$postForm.find('textarea:not([name="body"]),input[type="hidden"]:not(.captcha_cookie)').removeAttr('id').appendTo($dummyStuff);
|
||||||
|
|
||||||
$postForm.find('br').remove();
|
$postForm.find('br').remove();
|
||||||
$postForm.find('table').prepend('<tr><th colspan="2">\
|
$postForm.find('table').prepend('<tr><th colspan="2">\
|
||||||
<span class="handle">\
|
<span class="handle">\
|
||||||
<a class="close-btn" href="javascript:void(0)">X</a>\
|
<a class="close-btn" href="javascript:void(0)">×</a>\
|
||||||
' + _('Quick Reply') + '\
|
' + _('Quick Reply') + '\
|
||||||
</span>\
|
</span>\
|
||||||
</th></tr>');
|
</th></tr>');
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onready(function(){
|
onready(function(){
|
||||||
|
if($('body').hasClass('active-ukko')) return;
|
||||||
var showBackLinks = function() {
|
var showBackLinks = function() {
|
||||||
var reply_id = $(this).attr('id').replace(/^reply_/, '');
|
var reply_id = $(this).attr('id').replace(/(^reply_)|(^op_)/, '');
|
||||||
|
|
||||||
$(this).find('div.body a:not([rel="nofollow"])').each(function() {
|
$(this).find('div.body a:not([rel="nofollow"])').each(function() {
|
||||||
var id, post, $mentioned;
|
var id, post, $mentioned;
|
||||||
|
@ -26,8 +27,11 @@ onready(function(){
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$post = $('#reply_' + id);
|
$post = $('#reply_' + id);
|
||||||
|
if($post.length == 0){
|
||||||
|
$post = $('#op_' + id);
|
||||||
if($post.length == 0)
|
if($post.length == 0)
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$mentioned = $post.find('p.intro span.mentioned');
|
$mentioned = $post.find('p.intro span.mentioned');
|
||||||
if($mentioned.length == 0)
|
if($mentioned.length == 0)
|
||||||
|
@ -47,14 +51,12 @@ onready(function(){
|
||||||
};
|
};
|
||||||
|
|
||||||
$('div.post.reply').each(showBackLinks);
|
$('div.post.reply').each(showBackLinks);
|
||||||
|
$('div.post.op').each(showBackLinks);
|
||||||
|
|
||||||
$(document).on('new_post', function(e, post) {
|
$(document).on('new_post', function(e, post) {
|
||||||
if ($(post).hasClass("reply")) {
|
|
||||||
showBackLinks.call(post);
|
showBackLinks.call(post);
|
||||||
}
|
if ($(post).hasClass("op")) {
|
||||||
else {
|
|
||||||
$(post).find('div.post.reply').each(showBackLinks);
|
$(post).find('div.post.reply').each(showBackLinks);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ var update_own = function() {
|
||||||
|
|
||||||
if (posts[board] && posts[board].indexOf(id) !== -1) { // Own post!
|
if (posts[board] && posts[board].indexOf(id) !== -1) { // Own post!
|
||||||
$(this).addClass('you');
|
$(this).addClass('you');
|
||||||
$(this).find('span.name').first().append(' '+_('(You)'));
|
$(this).find('span.name').first().append(' <span class="own_post">'+_('(You)')+'</span>');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update references
|
// Update references
|
||||||
|
|
|
@ -28,10 +28,10 @@ watchlist.render = function(reset) {
|
||||||
JSON.parse(localStorage.watchlist).forEach(function(e, i) {
|
JSON.parse(localStorage.watchlist).forEach(function(e, i) {
|
||||||
//look at line 69, that's what (e) is here.
|
//look at line 69, that's what (e) is here.
|
||||||
threads.push('<div class="watchlist-inner" id="watchlist-'+i+'">' +
|
threads.push('<div class="watchlist-inner" id="watchlist-'+i+'">' +
|
||||||
'<span>Board: '+e[0]+'</span> ' +
|
'<span>/'+e[0]+'/ - ' +
|
||||||
'<span>Thread: '+'<a href="'+e[3]+'">'+e[1]+'</a></span> ' +
|
'<a href="'+e[3]+'">'+e[1].replace("thread_", _("Thread #"))+'</a>' +
|
||||||
'<span>Replies: '+e[2]+'</span> ' +
|
' ('+e[2]+') </span>' +
|
||||||
'<a class="watchlist-remove">[Unwatch]</a>'+
|
'<a class="watchlist-remove">X</a>'+
|
||||||
'</div>');
|
'</div>');
|
||||||
});
|
});
|
||||||
if ($('#watchlist').length) {
|
if ($('#watchlist').length) {
|
||||||
|
@ -40,17 +40,15 @@ watchlist.render = function(reset) {
|
||||||
$('#watchlist').append(threads.join(''));
|
$('#watchlist').append(threads.join(''));
|
||||||
} else {
|
} else {
|
||||||
//If the watchlist has not yet been rendered, create it.
|
//If the watchlist has not yet been rendered, create it.
|
||||||
$('form[name="post"]').before(
|
var menuStyle = getComputedStyle($('.boardlist')[0]);
|
||||||
|
$((active_page == 'ukko') ? 'hr:first' : (active_page == 'catalog') ? 'body>span:first' : 'form[name="post"]').before(
|
||||||
$('<div id="watchlist">'+
|
$('<div id="watchlist">'+
|
||||||
'<div class="watchlist-controls">'+
|
'<div class="watchlist-controls">'+
|
||||||
'<span><a id="clearList">[Clear List]</a></span> '+
|
'<span><a id="clearList">['+_('Clear List')+']</a></span> '+
|
||||||
'<span><a id="clearGhosts">[Clear Ghosts]</a></span>'+
|
'<span><a id="clearGhosts">['+_('Clear Ghosts')+']</a></span>'+
|
||||||
'</div>'+
|
'</div>'+
|
||||||
threads.join('')+
|
threads.join('')+
|
||||||
'</div>').css({
|
'</div>').css("background-color", menuStyle.backgroundColor).css("border", menuStyle.borderBottomWidth+" "+menuStyle.borderBottomStyle+" "+menuStyle.borderBottomColor));
|
||||||
background: $('.reply').css('background'),
|
|
||||||
borderColor : $('.reply').css('border-color')
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -62,6 +60,8 @@ watchlist.render = function(reset) {
|
||||||
watchlist.add = function(sel) {
|
watchlist.add = function(sel) {
|
||||||
var threadName, threadInfo;
|
var threadName, threadInfo;
|
||||||
|
|
||||||
|
var board_name = $(sel).parents('.thread').data('board');
|
||||||
|
|
||||||
if (active_page === 'thread') {
|
if (active_page === 'thread') {
|
||||||
if ($('.subject').length){
|
if ($('.subject').length){
|
||||||
//If a subject is given, use the first 20 characters as the thread name.
|
//If a subject is given, use the first 20 characters as the thread name.
|
||||||
|
@ -72,7 +72,7 @@ watchlist.add = function(sel) {
|
||||||
//board name, thread name as defined above, current amount of posts, thread url
|
//board name, thread name as defined above, current amount of posts, thread url
|
||||||
threadInfo = [board_name, threadName, $('.post').length, location.href];
|
threadInfo = [board_name, threadName, $('.post').length, location.href];
|
||||||
|
|
||||||
} else if (active_page === 'index') {
|
} else if (active_page === 'index' || active_page === 'ukko') {
|
||||||
|
|
||||||
var postCount;
|
var postCount;
|
||||||
//Figure out the post count.
|
//Figure out the post count.
|
||||||
|
@ -81,8 +81,8 @@ watchlist.add = function(sel) {
|
||||||
} else {
|
} else {
|
||||||
postCount = $(sel).parents('.op').siblings('.post').length+1;
|
postCount = $(sel).parents('.op').siblings('.post').length+1;
|
||||||
}
|
}
|
||||||
//Grab the reply link.
|
//Grab the reply link.;
|
||||||
var threadLink = $(sel).siblings('a:contains("[Reply]")').attr('href');
|
var threadLink = $(sel).siblings('a:not(.watchThread)').last().attr('href');
|
||||||
//Figure out the thread name. If anon, use the thread id.
|
//Figure out the thread name. If anon, use the thread id.
|
||||||
if ($(sel).parent().find('.subject').length) {
|
if ($(sel).parent().find('.subject').length) {
|
||||||
threadName = $(sel).parent().find('.subject').text().substring(0,20);
|
threadName = $(sel).parent().find('.subject').text().substring(0,20);
|
||||||
|
@ -144,16 +144,21 @@ watchlist.exists = function(sel) {
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
if (!(active_page == 'thread' || active_page == 'index' || active_page == 'catalog' || active_page == 'ukko')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Append the watchlist toggle button.
|
//Append the watchlist toggle button.
|
||||||
$('.boardlist').append('<span>[ <a id="watchlist-toggle">watchlist</a> ]</span>');
|
$('.boardlist').append('<span>[ <a class="watchlist-toggle" href="#">'+_('watchlist')+'</a> ]</span>');
|
||||||
//Append a watch thread button after every OP.
|
//Append a watch thread button after every OP.
|
||||||
$('.op>.intro').append('<a class="watchThread">[Watch Thread]</a>');
|
$('.op>.intro').append('<a class="watchThread" href="#">['+_('Watch Thread')+']</a>');
|
||||||
|
|
||||||
//Draw the watchlist, hidden.
|
//Draw the watchlist, hidden.
|
||||||
watchlist.render();
|
watchlist.render();
|
||||||
|
|
||||||
//Show or hide the watchlist.
|
//Show or hide the watchlist.
|
||||||
$('#watchlist-toggle').on('click', function(e) {
|
$('.watchlist-toggle').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
//if ctrl+click, reset the watchlist.
|
//if ctrl+click, reset the watchlist.
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
watchlist.render(true);
|
watchlist.render(true);
|
||||||
|
@ -167,7 +172,8 @@ $(document).ready(function(){
|
||||||
|
|
||||||
//Trigger the watchlist add function.
|
//Trigger the watchlist add function.
|
||||||
//The selector is passed as an argument in case the page is not a thread.
|
//The selector is passed as an argument in case the page is not a thread.
|
||||||
$('.watchThread').on('click', function() {
|
$('.watchThread').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
watchlist.add(this).render();
|
watchlist.add(this).render();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ $(document).ready(function(){
|
||||||
$('<style type="text/css"> img.hidden{ opacity: 0.1; background: grey; border: 1px solid #000; } </style>').appendTo($('head'));
|
$('<style type="text/css"> img.hidden{ opacity: 0.1; background: grey; border: 1px solid #000; } </style>').appendTo($('head'));
|
||||||
|
|
||||||
var hideImage = function() {
|
var hideImage = function() {
|
||||||
if ($(this).parent()[0].dataset.expanded == 'true') {
|
if ($(this).parent().data('expanded') == 'true') {
|
||||||
$(this).parent().click();
|
$(this).parent().click();
|
||||||
}
|
}
|
||||||
$(this)
|
$(this)
|
||||||
|
|
24
log.php
Normal file
24
log.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
include 'inc/functions.php';
|
||||||
|
include 'inc/mod/pages.php';
|
||||||
|
|
||||||
|
if (!isset($_GET['board']) || !preg_match("/{$config['board_regex']}/u", $_GET['board'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
error('Bad board.');
|
||||||
|
}
|
||||||
|
if (!openBoard($_GET['board'])) {
|
||||||
|
http_response_code(404);
|
||||||
|
error('No board.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config['public_logs'] == 0) error('This board has public logs disabled. Ask the board owner to enable it.');
|
||||||
|
if ($config['public_logs'] == 1) $hide_names = false;
|
||||||
|
if ($config['public_logs'] == 2) $hide_names = true;
|
||||||
|
|
||||||
|
if (!isset($_GET['page'])) {
|
||||||
|
$page = 1;
|
||||||
|
} else {
|
||||||
|
$page = (int)$_GET['page'];
|
||||||
|
};
|
||||||
|
|
||||||
|
mod_board_log($board['uri'], $page, $hide_names, true);
|
41
mod.php
41
mod.php
|
@ -5,22 +5,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once 'inc/functions.php';
|
require_once 'inc/functions.php';
|
||||||
require_once 'inc/bans.php';
|
|
||||||
require_once 'inc/mod/pages.php';
|
|
||||||
require_once 'inc/mod/auth.php';
|
|
||||||
|
|
||||||
if ($config['debug'])
|
if ($config['debug'])
|
||||||
$parse_start_time = microtime(true);
|
$parse_start_time = microtime(true);
|
||||||
|
|
||||||
// Fix for magic quotes
|
require_once 'inc/bans.php';
|
||||||
if (get_magic_quotes_gpc()) {
|
require_once 'inc/mod/pages.php';
|
||||||
function strip_array($var) {
|
|
||||||
return is_array($var) ? array_map('strip_array', $var) : stripslashes($var);
|
|
||||||
}
|
|
||||||
|
|
||||||
$_GET = strip_array($_GET);
|
check_login(true);
|
||||||
$_POST = strip_array($_POST);
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = isset($_SERVER['QUERY_STRING']) ? rawurldecode($_SERVER['QUERY_STRING']) : '';
|
$query = isset($_SERVER['QUERY_STRING']) ? rawurldecode($_SERVER['QUERY_STRING']) : '';
|
||||||
|
|
||||||
|
@ -41,11 +33,19 @@ $pages = array(
|
||||||
|
|
||||||
'/log' => 'log', // modlog
|
'/log' => 'log', // modlog
|
||||||
'/log/(\d+)' => 'log', // modlog
|
'/log/(\d+)' => 'log', // modlog
|
||||||
'/log:([^/]+)' => 'user_log', // modlog
|
'/log:([^/:]+)' => 'user_log', // modlog
|
||||||
'/log:([^/]+)/(\d+)' => 'user_log', // modlog
|
'/log:([^/:]+)/(\d+)' => 'user_log', // modlog
|
||||||
'/news' => 'secure_POST news', // view news
|
'/log:b:([^/]+)' => 'board_log', // modlog
|
||||||
'/news/(\d+)' => 'secure_POST news', // view news
|
'/log:b:([^/]+)/(\d+)' => 'board_log', // modlog
|
||||||
'/news/delete/(\d+)' => 'secure news_delete', // delete from news
|
|
||||||
|
'/edit_news' => 'secure_POST news', // view news
|
||||||
|
'/edit_news/(\d+)' => 'secure_POST news', // view news
|
||||||
|
'/edit_news/delete/(\d+)' => 'secure news_delete', // delete from news
|
||||||
|
|
||||||
|
'/edit_pages(?:/?(\%b)?)' => 'secure_POST pages',
|
||||||
|
'/edit_page/(\d+)' => 'secure_POST edit_page',
|
||||||
|
'/edit_pages/delete/([a-z0-9]+)' => 'secure delete_page',
|
||||||
|
'/edit_pages/delete/([a-z0-9]+)/(\%b)' => 'secure delete_page_board',
|
||||||
|
|
||||||
'/noticeboard' => 'secure_POST noticeboard', // view noticeboard
|
'/noticeboard' => 'secure_POST noticeboard', // view noticeboard
|
||||||
'/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard
|
'/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard
|
||||||
|
@ -82,6 +82,7 @@ $pages = array(
|
||||||
'/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address
|
'/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address
|
||||||
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
|
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
|
||||||
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread
|
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread
|
||||||
|
'/(\%b)/(un)?cycle/(\d+)' => 'secure cycle', // cycle thread
|
||||||
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread
|
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread
|
||||||
|
|
||||||
'/themes' => 'themes_list', // manage themes
|
'/themes' => 'themes_list', // manage themes
|
||||||
|
@ -93,10 +94,10 @@ $pages = array(
|
||||||
'/config/(\%b)' => 'secure_POST config', // config editor
|
'/config/(\%b)' => 'secure_POST config', // config editor
|
||||||
|
|
||||||
// these pages aren't listed in the dashboard without $config['debug']
|
// these pages aren't listed in the dashboard without $config['debug']
|
||||||
'/debug/antispam' => 'debug_antispam',
|
//'/debug/antispam' => 'debug_antispam',
|
||||||
'/debug/recent' => 'debug_recent_posts',
|
//'/debug/recent' => 'debug_recent_posts',
|
||||||
'/debug/apc' => 'debug_apc',
|
//'/debug/apc' => 'debug_apc',
|
||||||
'/debug/sql' => 'secure_POST debug_sql',
|
//'/debug/sql' => 'secure_POST debug_sql',
|
||||||
|
|
||||||
// This should always be at the end:
|
// This should always be at the end:
|
||||||
'/(\%b)/' => 'view_board',
|
'/(\%b)/' => 'view_board',
|
||||||
|
|
406
post.php
406
post.php
|
@ -7,20 +7,174 @@ require_once 'inc/functions.php';
|
||||||
require_once 'inc/anti-bot.php';
|
require_once 'inc/anti-bot.php';
|
||||||
require_once 'inc/bans.php';
|
require_once 'inc/bans.php';
|
||||||
|
|
||||||
// Fix for magic quotes
|
|
||||||
if (get_magic_quotes_gpc()) {
|
|
||||||
function strip_array($var) {
|
|
||||||
return is_array($var) ? array_map('strip_array', $var) : stripslashes($var);
|
|
||||||
}
|
|
||||||
|
|
||||||
$_GET = strip_array($_GET);
|
|
||||||
$_POST = strip_array($_POST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!isset($_POST['mod']) || !$_POST['mod']) && $config['board_locked']) {
|
if ((!isset($_POST['mod']) || !$_POST['mod']) && $config['board_locked']) {
|
||||||
error("Board is locked");
|
error("Board is locked");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dropped_post = false;
|
||||||
|
|
||||||
|
// Is it a post coming from NNTP? Let's extract it and pretend it's a normal post.
|
||||||
|
if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) {
|
||||||
|
if ($_SERVER['REMOTE_ADDR'] != $config['nntpchan']['trusted_peer']) {
|
||||||
|
error("NNTPChan: Forbidden. $_SERVER[REMOTE_ADDR] is not a trusted peer");
|
||||||
|
}
|
||||||
|
|
||||||
|
$_POST = array();
|
||||||
|
$_POST['json_response'] = true;
|
||||||
|
|
||||||
|
$headers = json_encode($_GET);
|
||||||
|
|
||||||
|
if (!isset ($_GET['Message-Id'])) {
|
||||||
|
if (!isset ($_GET['Message-ID'])) {
|
||||||
|
error("NNTPChan: No message ID");
|
||||||
|
}
|
||||||
|
else $msgid = $_GET['Message-ID'];
|
||||||
|
}
|
||||||
|
else $msgid = $_GET['Message-Id'];
|
||||||
|
|
||||||
|
$groups = preg_split("/,\s*/", $_GET['Newsgroups']);
|
||||||
|
if (count($groups) != 1) {
|
||||||
|
error("NNTPChan: Messages can go to only one newsgroup");
|
||||||
|
}
|
||||||
|
$group = $groups[0];
|
||||||
|
|
||||||
|
if (!isset($config['nntpchan']['dispatch'][$group])) {
|
||||||
|
error("NNTPChan: We don't synchronize $group");
|
||||||
|
}
|
||||||
|
$xboard = $config['nntpchan']['dispatch'][$group];
|
||||||
|
|
||||||
|
$ref = null;
|
||||||
|
if (isset ($_GET['References'])) {
|
||||||
|
$refs = preg_split("/,\s*/", $_GET['References']);
|
||||||
|
|
||||||
|
if (count($refs) > 1) {
|
||||||
|
error("NNTPChan: We don't support multiple references");
|
||||||
|
}
|
||||||
|
|
||||||
|
$ref = $refs[0];
|
||||||
|
|
||||||
|
$query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref");
|
||||||
|
$query->bindValue(':ref', $ref);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
$ary = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (count($ary) == 0) {
|
||||||
|
error("NNTPChan: We don't have $ref that $msgid references");
|
||||||
|
}
|
||||||
|
|
||||||
|
$p_id = $ary[0]['id'];
|
||||||
|
$p_board = $ary[0]['board'];
|
||||||
|
|
||||||
|
if ($p_board != $xboard) {
|
||||||
|
error("NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
$_POST['thread'] = $p_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$date = isset($_GET['Date']) ? strtotime($_GET['Date']) : time();
|
||||||
|
|
||||||
|
list($ct) = explode('; ', $_GET['Content-Type']);
|
||||||
|
|
||||||
|
$query = prepare("SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid");
|
||||||
|
$query->bindValue(":msgid", $msgid);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
$a = $query->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($a['c'] > 0) {
|
||||||
|
error("NNTPChan: We already have this post. Post discarded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ct == 'text/plain') {
|
||||||
|
$content = file_get_contents("php://input");
|
||||||
|
}
|
||||||
|
elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') {
|
||||||
|
_syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); // Debug
|
||||||
|
|
||||||
|
$content = '';
|
||||||
|
|
||||||
|
$newfiles = array();
|
||||||
|
foreach ($_FILES['attachment']['error'] as $id => $error) {
|
||||||
|
if ($_FILES['attachment']['type'][$id] == 'text/plain') {
|
||||||
|
$content .= file_get_contents($_FILES['attachment']['tmp_name'][$id]);
|
||||||
|
}
|
||||||
|
elseif ($_FILES['attachment']['type'][$id] == 'message/rfc822') { // Signed message, ignore for now
|
||||||
|
}
|
||||||
|
else { // A real attachment :^)
|
||||||
|
$file = array();
|
||||||
|
$file['name'] = $_FILES['attachment']['name'][$id];
|
||||||
|
$file['type'] = $_FILES['attachment']['type'][$id];
|
||||||
|
$file['size'] = $_FILES['attachment']['size'][$id];
|
||||||
|
$file['tmp_name'] = $_FILES['attachment']['tmp_name'][$id];
|
||||||
|
$file['error'] = $_FILES['attachment']['error'][$id];
|
||||||
|
|
||||||
|
$newfiles["file$id"] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_FILES = $newfiles;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error("NNTPChan: Wrong mime type: $ct");
|
||||||
|
}
|
||||||
|
|
||||||
|
$_POST['subject'] = isset($_GET['Subject']) ? ($_GET['Subject'] == 'None' ? '' : $_GET['Subject']) : '';
|
||||||
|
$_POST['board'] = $xboard;
|
||||||
|
|
||||||
|
if (isset ($_GET['From'])) {
|
||||||
|
list($name, $mail) = explode(" <", $_GET['From'], 2);
|
||||||
|
$mail = preg_replace('/>\s+$/', '', $mail);
|
||||||
|
|
||||||
|
$_POST['name'] = $name;
|
||||||
|
//$_POST['email'] = $mail;
|
||||||
|
$_POST['email'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset ($_GET['X_Sage'])) {
|
||||||
|
$_POST['email'] = 'sage';
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = preg_replace_callback('/>>([0-9a-fA-F]{6,})/', function($id) use ($xboard) {
|
||||||
|
$id = $id[1];
|
||||||
|
|
||||||
|
$query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule");
|
||||||
|
$idx = $id . "%";
|
||||||
|
$query->bindValue(':rule', $idx);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
$ary = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
if (count($ary) == 0) {
|
||||||
|
return ">>>>$id";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ret = array();
|
||||||
|
foreach ($ary as $v) {
|
||||||
|
if ($v['board'] != $xboard) {
|
||||||
|
$ret[] = ">>>/".$v['board']."/".$v['id'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ret[] = ">>".$v['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode($ret, ", ");
|
||||||
|
}
|
||||||
|
}, $content);
|
||||||
|
|
||||||
|
$_POST['body'] = $content;
|
||||||
|
|
||||||
|
$dropped_post = array(
|
||||||
|
'date' => $date,
|
||||||
|
'board' => $xboard,
|
||||||
|
'msgid' => $msgid,
|
||||||
|
'headers' => $headers,
|
||||||
|
'from_nntp' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
elseif (isset($_GET['Newsgroups'])) {
|
||||||
|
error("NNTPChan: NNTPChan support is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_POST['delete'])) {
|
if (isset($_POST['delete'])) {
|
||||||
// Delete
|
// Delete
|
||||||
|
|
||||||
|
@ -56,7 +210,7 @@ if (isset($_POST['delete'])) {
|
||||||
error($config['error']['nodelete']);
|
error($config['error']['nodelete']);
|
||||||
|
|
||||||
foreach ($delete as &$id) {
|
foreach ($delete as &$id) {
|
||||||
$query = prepare(sprintf("SELECT `thread`, `time`,`password` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
$query = prepare(sprintf("SELECT `id`,`thread`,`time`,`password` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
||||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
@ -80,9 +234,11 @@ if (isset($_POST['delete'])) {
|
||||||
if (isset($_POST['file'])) {
|
if (isset($_POST['file'])) {
|
||||||
// Delete just the file
|
// Delete just the file
|
||||||
deleteFile($id);
|
deleteFile($id);
|
||||||
|
modLog("User deleted file from his own post #$id");
|
||||||
} else {
|
} else {
|
||||||
// Delete entire post
|
// Delete entire post
|
||||||
deletePost($id);
|
deletePost($id);
|
||||||
|
modLog("User deleted his own post #$id");
|
||||||
}
|
}
|
||||||
|
|
||||||
_syslog(LOG_INFO, 'Deleted post: ' .
|
_syslog(LOG_INFO, 'Deleted post: ' .
|
||||||
|
@ -132,22 +288,48 @@ if (isset($_POST['delete'])) {
|
||||||
if (empty($report))
|
if (empty($report))
|
||||||
error($config['error']['noreport']);
|
error($config['error']['noreport']);
|
||||||
|
|
||||||
|
if (strlen($report) > 30)
|
||||||
|
error($config['error']['invalidreport']);
|
||||||
|
|
||||||
if (count($report) > $config['report_limit'])
|
if (count($report) > $config['report_limit'])
|
||||||
error($config['error']['toomanyreports']);
|
error($config['error']['toomanyreports']);
|
||||||
|
|
||||||
|
if ($config['report_captcha'] && !isset($_POST['captcha_text'], $_POST['captcha_cookie'])) {
|
||||||
|
error($config['error']['bot']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config['report_captcha']) {
|
||||||
|
$resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([
|
||||||
|
'mode' => 'check',
|
||||||
|
'text' => $_POST['captcha_text'],
|
||||||
|
'extra' => $config['captcha']['extra'],
|
||||||
|
'cookie' => $_POST['captcha_cookie']
|
||||||
|
]));
|
||||||
|
|
||||||
|
if ($resp !== '1') {
|
||||||
|
error($config['error']['captcha']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$reason = escape_markup_modifiers($_POST['reason']);
|
$reason = escape_markup_modifiers($_POST['reason']);
|
||||||
markup($reason);
|
markup($reason);
|
||||||
|
|
||||||
foreach ($report as &$id) {
|
foreach ($report as &$id) {
|
||||||
$query = prepare(sprintf("SELECT `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
$query = prepare(sprintf("SELECT `id`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
||||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
$query->execute() or error(db_error($query));
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
$thread = $query->fetchColumn();
|
$post = $query->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$error = event('report', array('ip' => $_SERVER['REMOTE_ADDR'], 'board' => $board['uri'], 'post' => $post, 'reason' => $reason, 'link' => link_for($post)));
|
||||||
|
|
||||||
|
if ($error) {
|
||||||
|
error($error);
|
||||||
|
}
|
||||||
|
|
||||||
if ($config['syslog'])
|
if ($config['syslog'])
|
||||||
_syslog(LOG_INFO, 'Reported post: ' .
|
_syslog(LOG_INFO, 'Reported post: ' .
|
||||||
'/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($thread ? '#' . $id : '') .
|
'/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') .
|
||||||
' for "' . $reason . '"'
|
' for "' . $reason . '"'
|
||||||
);
|
);
|
||||||
$query = prepare("INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason)");
|
$query = prepare("INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason)");
|
||||||
|
@ -163,13 +345,14 @@ if (isset($_POST['delete'])) {
|
||||||
$root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
|
$root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
|
||||||
|
|
||||||
if (!isset($_POST['json_response'])) {
|
if (!isset($_POST['json_response'])) {
|
||||||
header('Location: ' . $root . $board['dir'] . $config['file_index'], true, $config['redirect_http']);
|
$index = $root . $board['dir'] . $config['file_index'];
|
||||||
|
echo Element('page.html', array('config' => $config, 'body' => '<div style="text-align:center"><a href="javascript:window.close()">[ ' . _('Close window') ." ]</a> <a href='$index'>[ " . _('Return') . ' ]</a></div>', 'title' => _('Report submitted!')));
|
||||||
} else {
|
} else {
|
||||||
header('Content-Type: text/json');
|
header('Content-Type: text/json');
|
||||||
echo json_encode(array('success' => true));
|
echo json_encode(array('success' => true));
|
||||||
}
|
}
|
||||||
} elseif (isset($_POST['post'])) {
|
} elseif (isset($_POST['post']) || $dropped_post) {
|
||||||
if (!isset($_POST['body'], $_POST['board']))
|
if (!isset($_POST['body'], $_POST['board']) && !$dropped_post)
|
||||||
error($config['error']['bot']);
|
error($config['error']['bot']);
|
||||||
|
|
||||||
$post = array('board' => $_POST['board'], 'files' => array());
|
$post = array('board' => $_POST['board'], 'files' => array());
|
||||||
|
@ -196,19 +379,36 @@ if (isset($_POST['delete'])) {
|
||||||
} else
|
} else
|
||||||
$post['op'] = true;
|
$post['op'] = true;
|
||||||
|
|
||||||
|
|
||||||
|
if (!$dropped_post) {
|
||||||
// Check for CAPTCHA right after opening the board so the "return" link is in there
|
// Check for CAPTCHA right after opening the board so the "return" link is in there
|
||||||
if ($config['recaptcha']) {
|
if ($config['recaptcha']) {
|
||||||
if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field']))
|
if (!isset($_POST['g-recaptcha-response']))
|
||||||
error($config['error']['bot']);
|
error($config['error']['bot']);
|
||||||
|
|
||||||
// Check what reCAPTCHA has to say...
|
// Check what reCAPTCHA has to say...
|
||||||
$resp = recaptcha_check_answer($config['recaptcha_private'],
|
$resp = json_decode(file_get_contents(sprintf('https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s',
|
||||||
$_SERVER['REMOTE_ADDR'],
|
$config['recaptcha_private'],
|
||||||
$_POST['recaptcha_challenge_field'],
|
urlencode($_POST['g-recaptcha-response']),
|
||||||
$_POST['recaptcha_response_field']);
|
$_SERVER['REMOTE_ADDR'])), true);
|
||||||
if (!$resp->is_valid) {
|
|
||||||
|
if (!$resp['success']) {
|
||||||
error($config['error']['captcha']);
|
error($config['error']['captcha']);
|
||||||
}
|
}
|
||||||
|
// Same, but now with our custom captcha provider
|
||||||
|
if (($config['captcha']['enabled']) || (($post['op']) && ($config['new_thread_capt'])) ) {
|
||||||
|
$resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([
|
||||||
|
'mode' => 'check',
|
||||||
|
'text' => $_POST['captcha_text'],
|
||||||
|
'extra' => $config['captcha']['extra'],
|
||||||
|
'cookie' => $_POST['captcha_cookie']
|
||||||
|
]));
|
||||||
|
if ($resp !== '1') {
|
||||||
|
error($config['error']['captcha'] .
|
||||||
|
'<script>if (actually_load_captcha !== undefined) actually_load_captcha("'.$config['captcha']['provider_get'].'", "'.$config['captcha']['extra'].'");</script>');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
|
if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
|
||||||
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
|
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
|
||||||
|
@ -225,7 +425,7 @@ if (isset($_POST['delete'])) {
|
||||||
checkBan($board['uri']);
|
checkBan($board['uri']);
|
||||||
|
|
||||||
if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
|
if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
|
||||||
require 'inc/mod/auth.php';
|
check_login(false);
|
||||||
if (!$mod) {
|
if (!$mod) {
|
||||||
// Liar. You're not a mod.
|
// Liar. You're not a mod.
|
||||||
error($config['error']['notamod']);
|
error($config['error']['notamod']);
|
||||||
|
@ -252,10 +452,14 @@ if (isset($_POST['delete'])) {
|
||||||
if ($config['robot_enable'] && $config['robot_mute']) {
|
if ($config['robot_enable'] && $config['robot_mute']) {
|
||||||
checkMute();
|
checkMute();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$mod = $post['mod'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
//Check if thread exists
|
//Check if thread exists
|
||||||
if (!$post['op']) {
|
if (!$post['op']) {
|
||||||
$query = prepare(sprintf("SELECT `sticky`,`locked`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri']));
|
$query = prepare(sprintf("SELECT `sticky`,`locked`,`cycle`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri']));
|
||||||
$query->bindValue(':id', $post['thread'], PDO::PARAM_INT);
|
$query->bindValue(':id', $post['thread'], PDO::PARAM_INT);
|
||||||
$query->execute() or error(db_error());
|
$query->execute() or error(db_error());
|
||||||
|
|
||||||
|
@ -361,7 +565,9 @@ if (isset($_POST['delete'])) {
|
||||||
$post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email']));
|
$post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email']));
|
||||||
$post['body'] = $_POST['body'];
|
$post['body'] = $_POST['body'];
|
||||||
$post['password'] = $_POST['password'];
|
$post['password'] = $_POST['password'];
|
||||||
$post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || !empty($_FILES['file']['name'])));
|
$post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || count($_FILES) > 0));
|
||||||
|
|
||||||
|
if (!$dropped_post) {
|
||||||
|
|
||||||
if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
|
if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
|
||||||
$stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
|
$stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
|
||||||
|
@ -384,6 +590,12 @@ if (isset($_POST['delete'])) {
|
||||||
if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images'])
|
if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images'])
|
||||||
error($config['error']['image_hard_limit']);
|
error($config['error']['image_hard_limit']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!$post['op']) {
|
||||||
|
$numposts = numPosts($post['thread']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($post['has_file']) {
|
if ($post['has_file']) {
|
||||||
// Determine size sanity
|
// Determine size sanity
|
||||||
|
@ -432,7 +644,7 @@ if (isset($_POST['delete'])) {
|
||||||
|
|
||||||
$trip = generate_tripcode($post['name']);
|
$trip = generate_tripcode($post['name']);
|
||||||
$post['name'] = $trip[0];
|
$post['name'] = $trip[0];
|
||||||
$post['trip'] = isset($trip[1]) ? $trip[1] : '';
|
$post['trip'] = isset($trip[1]) ? $trip[1] : ''; // XX: Dropped posts and tripcodes
|
||||||
|
|
||||||
$noko = false;
|
$noko = false;
|
||||||
if (strtolower($post['email']) == 'noko') {
|
if (strtolower($post['email']) == 'noko') {
|
||||||
|
@ -447,7 +659,7 @@ if (isset($_POST['delete'])) {
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($_FILES as $key => $file) {
|
foreach ($_FILES as $key => $file) {
|
||||||
if ($file['size'] && $file['tmp_name']) {
|
if ($file['size'] && $file['tmp_name']) {
|
||||||
$file['filename'] = urldecode(get_magic_quotes_gpc() ? stripslashes($file['name']) : $file['name']);
|
$file['filename'] = urldecode($file['name']);
|
||||||
$file['extension'] = strtolower(mb_substr($file['filename'], mb_strrpos($file['filename'], '.') + 1));
|
$file['extension'] = strtolower(mb_substr($file['filename'], mb_strrpos($file['filename'], '.') + 1));
|
||||||
if (isset($config['filename_func']))
|
if (isset($config['filename_func']))
|
||||||
$file['file_id'] = $config['filename_func']($file);
|
$file['file_id'] = $config['filename_func']($file);
|
||||||
|
@ -467,6 +679,7 @@ if (isset($_POST['delete'])) {
|
||||||
|
|
||||||
if (empty($post['files'])) $post['has_file'] = false;
|
if (empty($post['files'])) $post['has_file'] = false;
|
||||||
|
|
||||||
|
if (!$dropped_post) {
|
||||||
// Check for a file
|
// Check for a file
|
||||||
if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
|
if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
|
||||||
if (!$post['has_file'] && $config['force_image_op'])
|
if (!$post['has_file'] && $config['force_image_op'])
|
||||||
|
@ -476,6 +689,7 @@ if (isset($_POST['delete'])) {
|
||||||
// Check for too many files
|
// Check for too many files
|
||||||
if (sizeof($post['files']) > $config['max_images'])
|
if (sizeof($post['files']) > $config['max_images'])
|
||||||
error($config['error']['toomanyimages']);
|
error($config['error']['toomanyimages']);
|
||||||
|
}
|
||||||
|
|
||||||
if ($config['strip_combining_chars']) {
|
if ($config['strip_combining_chars']) {
|
||||||
$post['name'] = strip_combining_chars($post['name']);
|
$post['name'] = strip_combining_chars($post['name']);
|
||||||
|
@ -484,6 +698,7 @@ if (isset($_POST['delete'])) {
|
||||||
$post['body'] = strip_combining_chars($post['body']);
|
$post['body'] = strip_combining_chars($post['body']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$dropped_post) {
|
||||||
// Check string lengths
|
// Check string lengths
|
||||||
if (mb_strlen($post['name']) > 35)
|
if (mb_strlen($post['name']) > 35)
|
||||||
error(sprintf($config['error']['toolong'], 'name'));
|
error(sprintf($config['error']['toolong'], 'name'));
|
||||||
|
@ -495,7 +710,7 @@ if (isset($_POST['delete'])) {
|
||||||
error($config['error']['toolong_body']);
|
error($config['error']['toolong_body']);
|
||||||
if (mb_strlen($post['password']) > 20)
|
if (mb_strlen($post['password']) > 20)
|
||||||
error(sprintf($config['error']['toolong'], 'password'));
|
error(sprintf($config['error']['toolong'], 'password'));
|
||||||
|
}
|
||||||
wordfilters($post['body']);
|
wordfilters($post['body']);
|
||||||
|
|
||||||
$post['body'] = escape_markup_modifiers($post['body']);
|
$post['body'] = escape_markup_modifiers($post['body']);
|
||||||
|
@ -504,6 +719,7 @@ if (isset($_POST['delete'])) {
|
||||||
$post['body'] .= "\n<tinyboard raw html>1</tinyboard>";
|
$post['body'] .= "\n<tinyboard raw html>1</tinyboard>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$dropped_post)
|
||||||
if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) {
|
if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) {
|
||||||
require 'inc/lib/geoip/geoip.inc';
|
require 'inc/lib/geoip/geoip.inc';
|
||||||
$gi=geoip\geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD);
|
$gi=geoip\geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD);
|
||||||
|
@ -545,6 +761,7 @@ if (isset($_POST['delete'])) {
|
||||||
$post['body'] .= "\n<tinyboard tag>" . $_POST['tag'] . "</tinyboard>";
|
$post['body'] .= "\n<tinyboard tag>" . $_POST['tag'] . "</tinyboard>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$dropped_post)
|
||||||
if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||||
$proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
$proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||||
$post['body'] .= "\n<tinyboard proxy>".$proxy."</tinyboard>";
|
$post['body'] .= "\n<tinyboard proxy>".$proxy."</tinyboard>";
|
||||||
|
@ -572,7 +789,12 @@ if (isset($_POST['delete'])) {
|
||||||
|
|
||||||
|
|
||||||
if ($post['has_file']) {
|
if ($post['has_file']) {
|
||||||
$fnarray = array();
|
$md5cmd = false;
|
||||||
|
if ($config['bsd_md5']) $md5cmd = '/sbin/md5 -r';
|
||||||
|
if ($config['gnu_md5']) $md5cmd = 'md5sum';
|
||||||
|
|
||||||
|
$allhashes = '';
|
||||||
|
|
||||||
foreach ($post['files'] as $key => &$file) {
|
foreach ($post['files'] as $key => &$file) {
|
||||||
if ($post['op'] && $config['allowed_ext_op']) {
|
if ($post['op'] && $config['allowed_ext_op']) {
|
||||||
if (!in_array($file['extension'], $config['allowed_ext_op']))
|
if (!in_array($file['extension'], $config['allowed_ext_op']))
|
||||||
|
@ -586,38 +808,33 @@ if (isset($_POST['delete'])) {
|
||||||
// Truncate filename if it is too long
|
// Truncate filename if it is too long
|
||||||
$file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']);
|
$file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']);
|
||||||
|
|
||||||
if (!isset($filenames)) {
|
|
||||||
$filenames = escapeshellarg($file['tmp_name']);
|
|
||||||
} else {
|
|
||||||
$filenames .= (' ' . escapeshellarg($file['tmp_name']));
|
|
||||||
}
|
|
||||||
|
|
||||||
$fnarray[] = $file['tmp_name'];
|
|
||||||
|
|
||||||
$upload = $file['tmp_name'];
|
$upload = $file['tmp_name'];
|
||||||
|
|
||||||
if (!is_readable($upload))
|
if (!is_readable($upload))
|
||||||
error($config['error']['nomove']);
|
error($config['error']['nomove']);
|
||||||
|
|
||||||
|
if ($md5cmd) {
|
||||||
|
$output = shell_exec_error($md5cmd . " " . escapeshellarg($upload));
|
||||||
|
$output = explode(' ', $output);
|
||||||
|
$hash = $output[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hash = md5_file($upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
$md5cmd = $config['bsd_md5'] ? 'md5 -r' : 'md5sum';
|
$file['hash'] = $hash;
|
||||||
|
$allhashes .= $hash;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$config['php_md5'] && $output = shell_exec_error("cat $filenames | $md5cmd")) {
|
if (count ($post['files']) == 1) {
|
||||||
$explodedvar = explode(' ', $output);
|
|
||||||
$hash = $explodedvar[0];
|
|
||||||
$post['filehash'] = $hash;
|
$post['filehash'] = $hash;
|
||||||
} elseif ($config['max_images'] === 1) {
|
|
||||||
$post['filehash'] = md5_file($upload);
|
|
||||||
} else {
|
|
||||||
$str_to_hash = '';
|
|
||||||
foreach ($fnarray as $i => $f) {
|
|
||||||
$str_to_hash .= file_get_contents($f);
|
|
||||||
}
|
}
|
||||||
$post['filehash'] = md5($str_to_hash);
|
else {
|
||||||
|
$post['filehash'] = md5($allhashes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) {
|
if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
|
||||||
require_once 'inc/filters.php';
|
require_once 'inc/filters.php';
|
||||||
|
|
||||||
do_filters($post);
|
do_filters($post);
|
||||||
|
@ -625,13 +842,15 @@ if (isset($_POST['delete'])) {
|
||||||
|
|
||||||
if ($post['has_file']) {
|
if ($post['has_file']) {
|
||||||
foreach ($post['files'] as $key => &$file) {
|
foreach ($post['files'] as $key => &$file) {
|
||||||
if ($file['is_an_image'] && $config['ie_mime_type_detection'] !== false) {
|
if ($file['is_an_image']) {
|
||||||
|
if ($config['ie_mime_type_detection'] !== false) {
|
||||||
// Check IE MIME type detection XSS exploit
|
// Check IE MIME type detection XSS exploit
|
||||||
$buffer = file_get_contents($upload, null, null, null, 255);
|
$buffer = file_get_contents($upload, null, null, null, 255);
|
||||||
if (preg_match($config['ie_mime_type_detection'], $buffer)) {
|
if (preg_match($config['ie_mime_type_detection'], $buffer)) {
|
||||||
undoImage($post);
|
undoImage($post);
|
||||||
error($config['error']['mime_exploit']);
|
error($config['error']['mime_exploit']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
require_once 'inc/image.php';
|
require_once 'inc/image.php';
|
||||||
|
|
||||||
|
@ -639,6 +858,9 @@ if (isset($_POST['delete'])) {
|
||||||
if (!$size = @getimagesize($file['tmp_name'])) {
|
if (!$size = @getimagesize($file['tmp_name'])) {
|
||||||
error($config['error']['invalidimg']);
|
error($config['error']['invalidimg']);
|
||||||
}
|
}
|
||||||
|
if (!in_array($size[2], array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_BMP))) {
|
||||||
|
error($config['error']['invalidimg']);
|
||||||
|
}
|
||||||
if ($size[0] > $config['max_width'] || $size[1] > $config['max_height']) {
|
if ($size[0] > $config['max_width'] || $size[1] > $config['max_height']) {
|
||||||
error($config['error']['maxsize']);
|
error($config['error']['maxsize']);
|
||||||
}
|
}
|
||||||
|
@ -747,6 +969,34 @@ if (isset($_POST['delete'])) {
|
||||||
$file['thumbheight'] = $size[1];
|
$file['thumbheight'] = $size[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($config['tesseract_ocr'] && $file['thumb'] != 'file') { // Let's OCR it!
|
||||||
|
$fname = $file['tmp_name'];
|
||||||
|
|
||||||
|
if ($file['height'] > 500 || $file['width'] > 500) {
|
||||||
|
$fname = $file['thumb'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fname == 'spoiler') { // We don't have that much CPU time, do we?
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$tmpname = "tmp/tesseract/".rand(0,10000000);
|
||||||
|
|
||||||
|
// Preprocess command is an ImageMagick b/w quantization
|
||||||
|
$error = shell_exec_error(sprintf($config['tesseract_preprocess_command'], escapeshellarg($fname)) . " | " .
|
||||||
|
'tesseract stdin '.escapeshellarg($tmpname).' '.$config['tesseract_params']);
|
||||||
|
$tmpname .= ".txt";
|
||||||
|
|
||||||
|
$value = @file_get_contents($tmpname);
|
||||||
|
@unlink($tmpname);
|
||||||
|
|
||||||
|
if ($value && trim($value)) {
|
||||||
|
// This one has an effect, that the body is appended to a post body. So you can write a correct
|
||||||
|
// spamfilter.
|
||||||
|
$post['body_nomarkup'] .= "<tinyboard ocr image $key>".htmlspecialchars($value)."</tinyboard>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($dont_copy_file) || !$dont_copy_file) {
|
if (!isset($dont_copy_file) || !$dont_copy_file) {
|
||||||
if (isset($file['file_tmp'])) {
|
if (isset($file['file_tmp'])) {
|
||||||
if (!@rename($file['tmp_name'], $file['file']))
|
if (!@rename($file['tmp_name'], $file['file']))
|
||||||
|
@ -786,7 +1036,12 @@ if (isset($_POST['delete'])) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) {
|
// Do filters again if OCRing
|
||||||
|
if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
|
||||||
|
do_filters($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) {
|
||||||
undoImage($post);
|
undoImage($post);
|
||||||
if ($config['robot_mute']) {
|
if ($config['robot_mute']) {
|
||||||
error(sprintf($config['error']['muted'], mute()));
|
error(sprintf($config['error']['muted'], mute()));
|
||||||
|
@ -825,8 +1080,53 @@ if (isset($_POST['delete'])) {
|
||||||
$post['id'] = $id = post($post);
|
$post['id'] = $id = post($post);
|
||||||
$post['slug'] = slugify($post);
|
$post['slug'] = slugify($post);
|
||||||
|
|
||||||
|
|
||||||
|
if ($dropped_post && $dropped_post['from_nntp']) {
|
||||||
|
$query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ".
|
||||||
|
"(:board , :id , :message_id , :message_id_digest , false, :headers)");
|
||||||
|
|
||||||
|
$query->bindValue(':board', $dropped_post['board']);
|
||||||
|
$query->bindValue(':id', $id);
|
||||||
|
$query->bindValue(':message_id', $dropped_post['msgid']);
|
||||||
|
$query->bindValue(':message_id_digest', sha1($dropped_post['msgid']));
|
||||||
|
$query->bindValue(':headers', $dropped_post['headers']);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
} // ^^^^^ For inbound posts ^^^^^
|
||||||
|
elseif ($config['nntpchan']['enabled'] && $config['nntpchan']['group']) {
|
||||||
|
// vvvvv For outbound posts vvvvv
|
||||||
|
|
||||||
|
require_once('inc/nntpchan/nntpchan.php');
|
||||||
|
$msgid = gen_msgid($post['board'], $post['id']);
|
||||||
|
|
||||||
|
list($headers, $files) = post2nntp($post, $msgid);
|
||||||
|
|
||||||
|
$message = gen_nntp($headers, $files);
|
||||||
|
|
||||||
|
$query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ".
|
||||||
|
"(:board , :id , :message_id , :message_id_digest , true , :headers)");
|
||||||
|
|
||||||
|
$query->bindValue(':board', $post['board']);
|
||||||
|
$query->bindValue(':id', $post['id']);
|
||||||
|
$query->bindValue(':message_id', $msgid);
|
||||||
|
$query->bindValue(':message_id_digest', sha1($msgid));
|
||||||
|
$query->bindValue(':headers', json_encode($headers));
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
|
||||||
|
// Let's broadcast it!
|
||||||
|
nntp_publish($message, $msgid);
|
||||||
|
}
|
||||||
|
|
||||||
insertFloodPost($post);
|
insertFloodPost($post);
|
||||||
|
|
||||||
|
// Handle cyclical threads
|
||||||
|
if (!$post['op'] && isset($thread['cycle']) && $thread['cycle']) {
|
||||||
|
// Query is a bit weird due to "This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'" (MariaDB Ver 15.1 Distrib 10.0.17-MariaDB, for Linux (x86_64))
|
||||||
|
$query = prepare(sprintf('DELETE FROM ``posts_%s`` WHERE `thread` = :thread AND `id` NOT IN (SELECT `id` FROM (SELECT `id` FROM ``posts_%s`` WHERE `thread` = :thread ORDER BY `id` DESC LIMIT :limit) i)', $board['uri'], $board['uri']));
|
||||||
|
$query->bindValue(':thread', $post['thread']);
|
||||||
|
$query->bindValue(':limit', $config['cycle_limit'], PDO::PARAM_INT);
|
||||||
|
$query->execute() or error(db_error($query));
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($post['antispam_hash'])) {
|
if (isset($post['antispam_hash'])) {
|
||||||
incrementSpamHash($post['antispam_hash']);
|
incrementSpamHash($post['antispam_hash']);
|
||||||
}
|
}
|
||||||
|
@ -905,7 +1205,7 @@ if (isset($_POST['delete'])) {
|
||||||
$build_pages = range(1, $config['max_pages']);
|
$build_pages = range(1, $config['max_pages']);
|
||||||
|
|
||||||
if ($post['op'])
|
if ($post['op'])
|
||||||
clean();
|
clean($id);
|
||||||
|
|
||||||
event('post-after', $post);
|
event('post-after', $post);
|
||||||
|
|
||||||
|
|
19
report.php
Normal file
19
report.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
include 'inc/functions.php';
|
||||||
|
$global = isset($_GET['global']);
|
||||||
|
$post = (isset($_GET['post']) ? $_GET['post'] : false);
|
||||||
|
$board = (isset($_GET['board']) ? $_GET['board'] : false);
|
||||||
|
|
||||||
|
if (!$post || !preg_match('/^delete_\d+$/', $post) || !$board || !openBoard($board)) {
|
||||||
|
header('HTTP/1.1 400 Bad Request');
|
||||||
|
error(_('Bad request.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config['report_captcha']) {
|
||||||
|
$captcha = generate_captcha($config['captcha']['extra']);
|
||||||
|
} else {
|
||||||
|
$captcha = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = Element('report.html', ['global' => $global, 'post' => $post, 'board' => $board, 'captcha' => $captcha, 'config' => $config]);
|
||||||
|
echo Element('page.html', ['config' => $config, 'body' => $body]);
|
|
@ -15,7 +15,7 @@
|
||||||
$boards = listBoards(TRUE);
|
$boards = listBoards(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
$body = Element('search_form.html', Array('boards' => $boards, 'b' => isset($_GET['board']) ? $_GET['board'] : false, 'search' => isset($_GET['search']) ? str_replace('"', '"', utf8tohtml($_GET['search'])) : false));
|
$body = Element('search_form.html', Array('boards' => $boards, 'board' => isset($_GET['board']) ? $_GET['board'] : false, 'search' => isset($_GET['search']) ? str_replace('"', '"', utf8tohtml($_GET['search'])) : false));
|
||||||
|
|
||||||
if(isset($_GET['search']) && !empty($_GET['search']) && isset($_GET['board']) && in_array($_GET['board'], $boards)) {
|
if(isset($_GET['search']) && !empty($_GET['search']) && isset($_GET['board']) && in_array($_GET['board'], $boards)) {
|
||||||
$phrase = $_GET['search'];
|
$phrase = $_GET['search'];
|
||||||
|
|
162
smart_build.php
162
smart_build.php
|
@ -1,163 +1,30 @@
|
||||||
<?php
|
<?php
|
||||||
require_once("inc/functions.php");
|
require_once("inc/functions.php");
|
||||||
|
require_once("inc/route.php");
|
||||||
|
require_once("inc/controller.php");
|
||||||
|
|
||||||
if (!$config['smart_build']) {
|
if (!$config["smart_build_helper"]) {
|
||||||
die('You need to enable $config["smart_build"]');
|
die('You need to enable $config["smart_build_helper"]');
|
||||||
}
|
}
|
||||||
|
|
||||||
$config['smart_build'] = false; // Let's disable it, so we can build the page for real
|
$config['smart_build'] = false; // Let's disable it, so we can build the page for real
|
||||||
|
$config['generation_strategies'] = array('strategy_immediate');
|
||||||
|
|
||||||
function after_open_board() { global $config;
|
function after_open_board() { global $config;
|
||||||
$config['smart_build'] = false;
|
$config['smart_build'] = false;
|
||||||
|
$config['generation_strategies'] = array('strategy_immediate');
|
||||||
};
|
};
|
||||||
|
|
||||||
function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page;
|
|
||||||
if ($page < 1) return false;
|
|
||||||
if (!openBoard($b)) return false;
|
|
||||||
if ($page > $config['max_pages']) return false;
|
|
||||||
$config['try_smarter'] = true;
|
|
||||||
$build_pages = array($page);
|
|
||||||
buildIndex("skip");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_api_board($b, $page = 0) { $page = (int)$page;
|
|
||||||
return sb_board($b, $page + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread;
|
|
||||||
if ($thread < 1) return false;
|
|
||||||
|
|
||||||
if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false;
|
|
||||||
|
|
||||||
if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false;
|
|
||||||
|
|
||||||
$query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b));
|
|
||||||
if (!$query->execute()) return false;
|
|
||||||
|
|
||||||
$s = $query->fetch(PDO::FETCH_ASSOC);
|
|
||||||
$max = $s['max'];
|
|
||||||
|
|
||||||
if ($thread > $max) return false;
|
|
||||||
|
|
||||||
$query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b));
|
|
||||||
$query->bindValue(':id', $thread);
|
|
||||||
|
|
||||||
if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) {
|
|
||||||
Cache::set("thread_exists_".$b."_".$thread, "no");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($slugcheck && $config['slugify']) {
|
|
||||||
global $request;
|
|
||||||
|
|
||||||
$link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b));
|
|
||||||
$link = "/".$b."/".$config['dir']['res'].$link;
|
|
||||||
|
|
||||||
if ($link != $request) {
|
|
||||||
header("Location: $link", true, 301);
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway
|
|
||||||
global $request;
|
|
||||||
$r = str_replace("+50", "", $request);
|
|
||||||
$r = substr($r, 1); // Cut the slash
|
|
||||||
|
|
||||||
if (file_exists($r)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!openBoard($b)) return false;
|
|
||||||
buildThread($thread);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_thread_slugcheck($b, $thread) {
|
|
||||||
return sb_thread($b, $thread, true);
|
|
||||||
}
|
|
||||||
function sb_thread_slugcheck50($b, $thread) {
|
|
||||||
return sb_thread($b, $thread, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_api($b) { global $config, $build_pages;
|
|
||||||
if (!openBoard($b)) return false;
|
|
||||||
$config['try_smarter'] = true;
|
|
||||||
$build_pages = array(-1);
|
|
||||||
buildIndex();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_ukko() {
|
|
||||||
rebuildTheme("ukko", "post-thread");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_catalog($b) {
|
|
||||||
if (!openBoard($b)) return false;
|
|
||||||
|
|
||||||
rebuildTheme("catalog", "post-thread", $b);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_recent() {
|
|
||||||
rebuildTheme("recent", "post-thread");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sb_sitemap() {
|
|
||||||
rebuildTheme("sitemap", "all");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$entrypoints = array();
|
|
||||||
|
|
||||||
$entrypoints['/%b/'] = 'sb_board';
|
|
||||||
$entrypoints['/%b/'.$config['file_index']] = 'sb_board';
|
|
||||||
$entrypoints['/%b/'.$config['file_page']] = 'sb_board';
|
|
||||||
$entrypoints['/%b/%d.json'] = 'sb_api_board';
|
|
||||||
if ($config['api']['enabled']) {
|
|
||||||
$entrypoints['/%b/threads.json'] = 'sb_api';
|
|
||||||
$entrypoints['/%b/catalog.json'] = 'sb_api';
|
|
||||||
}
|
|
||||||
|
|
||||||
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck';
|
|
||||||
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50';
|
|
||||||
if ($config['slugify']) {
|
|
||||||
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck';
|
|
||||||
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50';
|
|
||||||
}
|
|
||||||
if ($config['api']['enabled']) {
|
|
||||||
$entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread';
|
|
||||||
}
|
|
||||||
|
|
||||||
$entrypoints['/*/'] = 'sb_ukko';
|
|
||||||
$entrypoints['/*/index.html'] = 'sb_ukko';
|
|
||||||
$entrypoints['/recent.html'] = 'sb_recent';
|
|
||||||
$entrypoints['/%b/catalog.html'] = 'sb_catalog';
|
|
||||||
$entrypoints['/sitemap.xml'] = 'sb_sitemap';
|
|
||||||
|
|
||||||
$reached = false;
|
|
||||||
|
|
||||||
$request = $_SERVER['REQUEST_URI'];
|
$request = $_SERVER['REQUEST_URI'];
|
||||||
list($request) = explode('?', $request);
|
|
||||||
|
|
||||||
foreach ($entrypoints as $id => $fun) {
|
$route = route($request);
|
||||||
$id = '@^' . preg_quote($id, '@') . '$@u';
|
|
||||||
|
|
||||||
$id = str_replace('%b', '('.$config['board_regex'].')', $id);
|
if (!$route) {
|
||||||
$id = str_replace('%d', '([0-9]+)', $id);
|
$reached = false;
|
||||||
$id = str_replace('%s', '[a-zA-Z0-9-]+', $id);
|
}
|
||||||
|
else {
|
||||||
$matches = null;
|
list ($fun, $args) = $route;
|
||||||
|
$reached = call_user_func_array($fun, $args);
|
||||||
if (preg_match ($id, $request, $matches)) {
|
|
||||||
array_shift($matches);
|
|
||||||
|
|
||||||
$reached = call_user_func_array($fun, $matches);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function die_404() { global $config;
|
function die_404() { global $config;
|
||||||
|
@ -194,6 +61,9 @@ if ($reached) {
|
||||||
elseif (preg_match('/\.xml$/', $request)) {
|
elseif (preg_match('/\.xml$/', $request)) {
|
||||||
header("Content-Type", "application/xml");
|
header("Content-Type", "application/xml");
|
||||||
}
|
}
|
||||||
|
elseif (preg_match('/\.rss$/', $request)) {
|
||||||
|
header("Content-Type", "application/rss+xml");
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
header("Content-Type", "text/html; charset=utf-8");
|
header("Content-Type", "text/html; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
222
stylesheets/greendark.css
Normal file
222
stylesheets/greendark.css
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/* greenddark.css by Z Blanche */
|
||||||
|
body {
|
||||||
|
background:#1b1b1b;
|
||||||
|
background-image: url(''), url('');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed, scroll;
|
||||||
|
background-position: right bottom, 100% 100%;
|
||||||
|
font-family: arial,helvetica,sans-serif;
|
||||||
|
font-size: 12pt;
|
||||||
|
color: #C0C0C0;
|
||||||
|
cursor: default;
|
||||||
|
margin: 0 8px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
div.boardlist {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.post.reply {
|
||||||
|
margin-right: px;
|
||||||
|
margin-bottom: px;
|
||||||
|
background: #222;
|
||||||
|
border: 0;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow:
|
||||||
|
display:
|
||||||
|
}
|
||||||
|
/*media screen*/
|
||||||
|
a, a:visited {
|
||||||
|
text-decoration: underline;
|
||||||
|
color:#008080
|
||||||
|
}
|
||||||
|
button, input[type=submit], input[type=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 0;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
h1, .subtitle a, .subtitle {
|
||||||
|
color: #0df211!important;
|
||||||
|
}
|
||||||
|
input[type="text"], textarea, input[type=password] {
|
||||||
|
background: #32d23;
|
||||||
|
border-radius: 25px;
|
||||||
|
}
|
||||||
|
form table tr th {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.post {
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
border-radius: 22px; }
|
||||||
|
hr {
|
||||||
|
opacity:0.2;
|
||||||
|
}
|
||||||
|
label, .subject{
|
||||||
|
color:#AAA!important;
|
||||||
|
}
|
||||||
|
.name{
|
||||||
|
color:#!important;
|
||||||
|
}
|
||||||
|
div.pages {
|
||||||
|
border:0;
|
||||||
|
background:none;
|
||||||
|
color: #fff!important;
|
||||||
|
}
|
||||||
|
div.pages a.selected {
|
||||||
|
color: #ff69b4!important;
|
||||||
|
padding: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.pages a {
|
||||||
|
color: #fafafa!important;
|
||||||
|
padding: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.subtitle a {
|
||||||
|
display: block;
|
||||||
|
margin: 7px auto 7px auto;
|
||||||
|
font-size: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid #0df211;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 7px;
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
input[type=text], input[type=password] {
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
resize:vertical;
|
||||||
|
max-height: 400px;
|
||||||
|
width: 250px;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
@-webkit-keyframes Pulse {
|
||||||
|
from { background-color: #007d9a;
|
||||||
|
-webkit-box-shadow: 0 0 9px #333; }
|
||||||
|
50% { background-color: #2daebf;
|
||||||
|
-webkit-box-shadow: 0 0 18px #2daebf; }
|
||||||
|
to { background-color: #007d9a;
|
||||||
|
-webkit-box-shadow: 0 0 9px #333; }
|
||||||
|
}
|
||||||
|
.board_image {
|
||||||
|
-webkit-animation-name: Pulse;
|
||||||
|
-webkit-animation-duration: 3s;
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
-webkit-transform: rotate(5deg);
|
||||||
|
}
|
||||||
|
@-webkit-keyframes shakey {
|
||||||
|
0% { -webkit-transform: translate(2px, 1px) rotate(0deg); }
|
||||||
|
10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); }
|
||||||
|
20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); }
|
||||||
|
30% { -webkit-transform: translate(0px, 2px) rotate(0deg); }
|
||||||
|
40% { -webkit-transform: translate(1px, -1px) rotate(1deg); }
|
||||||
|
50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); }
|
||||||
|
60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); }
|
||||||
|
70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); }
|
||||||
|
80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); }
|
||||||
|
90% { -webkit-transform: translate(2px, 2px) rotate(0deg); }
|
||||||
|
100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); }
|
||||||
|
}
|
||||||
|
button:hover, input[type=submit]:hover, input[type=button]:hover,
|
||||||
|
button:focus, input[type=submit]:focus, input[type=button]:focus
|
||||||
|
{
|
||||||
|
-webkit-animation-name: shakey;
|
||||||
|
-webkit-animation-duration: 0.1s;
|
||||||
|
-webkit-transform-origin:50% 50%;
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
-webkit-animation-timing-function: linear;
|
||||||
|
background-color: #2daebf;
|
||||||
|
}
|
||||||
|
button, input[type=submit], input[type=button] {
|
||||||
|
-webkit-animation-name: Pulse;
|
||||||
|
-webkit-animation-duration: 2s;
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
color: #ffd;
|
||||||
|
}
|
||||||
|
div.ban {
|
||||||
|
background: #222;
|
||||||
|
background-image: url(''), url('');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed, scroll;
|
||||||
|
background-position: right bottom, 100% 100%;
|
||||||
|
color:#fff;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
.desktop-style div.boardlist, .desktop-style div.boardlist:hover {
|
||||||
|
background:#333!important;
|
||||||
|
border: 0!important;
|
||||||
|
}
|
||||||
|
.theme-catalog div.grid-size-small:hover {
|
||||||
|
background: #333!important;
|
||||||
|
}
|
||||||
|
#options_div {
|
||||||
|
background: #222!important;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
background:#333;
|
||||||
|
color:#eee;
|
||||||
|
cursor:pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px #222 solid;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 8px;
|
||||||
|
background: #333;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #ddd;
|
||||||
|
width: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 10px;
|
||||||
|
-webkit-box-shadow: 0 0 6px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
header div.subtitle {
|
||||||
|
font-size: 12pt
|
||||||
|
}
|
||||||
|
.desktop-style div.boardlist, .desktop-style div.boardlist:hover {
|
||||||
|
background: #1b1b1b!important;
|
||||||
|
}
|
||||||
|
div.post.reply.highlighted {
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
div.banner {
|
||||||
|
background-color: #1b1b1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.blotter {
|
||||||
|
color: green;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.post.reply div.body a {
|
||||||
|
color: #AAA;
|
||||||
|
}
|
||||||
|
p.intro a.email span.name {
|
||||||
|
color: #cdaf95;
|
||||||
|
}
|
||||||
|
div.post.reply div.body a:link:hover, div.post.reply div.body a:visited:hover {
|
||||||
|
color: #32DD72;
|
||||||
|
}
|
||||||
|
p.intro span.capcode,p.intro a.capcode,p.intro a.nametag {
|
||||||
|
color: #F00000;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
table tbody tr:nth-of-type( even ) {
|
||||||
|
background-color: #1b1b1b;
|
||||||
|
}
|
BIN
stylesheets/img/fade-gray.png
Normal file
BIN
stylesheets/img/fade-gray.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 196 B |
401
stylesheets/sharp.css
Normal file
401
stylesheets/sharp.css
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
/**
|
||||||
|
* sharp.css
|
||||||
|
* For AwsumChan by Circlepuller
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
background: #FFF url('img/fade-gray.png') repeat-x 50% 0%;
|
||||||
|
color: black;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
|
margin: 0 8px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
table * {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
a:link, a:visited {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #AF0A0F;
|
||||||
|
}
|
||||||
|
a:link:hover, a:visited:hover {
|
||||||
|
color: #F00;
|
||||||
|
}
|
||||||
|
a.post_no {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.intro a.post_no, p.intro a.email {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.intro a.email span.name {
|
||||||
|
color: #34345C;
|
||||||
|
}
|
||||||
|
.intro a.email:hover span.name {
|
||||||
|
color: #F00;
|
||||||
|
}
|
||||||
|
.intro label {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #AF0A0F;
|
||||||
|
font-size: 11pt;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-family: tahoma, sans-serif;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
font-size: 20pt;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
h1.logo img {
|
||||||
|
display: inline;
|
||||||
|
float: none;
|
||||||
|
background-image: url('/static/logo_bg.gif');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
header div.subtitle, h1 {
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
header div.subtitle {
|
||||||
|
font-size: 8pt;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
margin-bottom: 4em;
|
||||||
|
}
|
||||||
|
form table {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
form table input {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
input[type="text"], input[type="password"], textarea {
|
||||||
|
border: 1px solid #a9a9a9;
|
||||||
|
text-indent: 0px;
|
||||||
|
text-shadow: none;
|
||||||
|
text-transform: none;
|
||||||
|
word-spacing: normal;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
form table tr td {
|
||||||
|
text-align: left;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
form table tr th {
|
||||||
|
text-align: left;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
form table tr th {
|
||||||
|
background: #AF0A0F;
|
||||||
|
color: #FFF;
|
||||||
|
border: 1px solid #800000;
|
||||||
|
}
|
||||||
|
form table tr td div {
|
||||||
|
text-align: center;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
form table tr td div input {
|
||||||
|
display: block;
|
||||||
|
margin: 2px auto 0 auto;
|
||||||
|
}
|
||||||
|
form table tr td div label {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
.unimportant, .unimportant * {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
p.fileinfo {
|
||||||
|
display: block;
|
||||||
|
margin: 0px;
|
||||||
|
padding-right: 7em;
|
||||||
|
}
|
||||||
|
div.banner {
|
||||||
|
background-color: #AF0A0F;
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
div.banner, div.banner a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.banner a:hover {
|
||||||
|
color: #EEF2FF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
img.banner, img.board_image {
|
||||||
|
float: none;
|
||||||
|
margin: 4px auto 0 auto;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
div.post img {
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px 20px 0 0;
|
||||||
|
}
|
||||||
|
div.post img.icon {
|
||||||
|
display: inline;
|
||||||
|
float: none;
|
||||||
|
margin: 0 5px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
div.post.op {
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
div.post.op hr {
|
||||||
|
border-color: #D9BFB7;
|
||||||
|
}
|
||||||
|
.intro {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
}
|
||||||
|
input.delete {
|
||||||
|
float: left;
|
||||||
|
margin: 1px 6px 0 0;
|
||||||
|
}
|
||||||
|
.intro span.subject {
|
||||||
|
color: #0F0C5D;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.intro span.name {
|
||||||
|
color: #117743;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.intro a.capcode, p.intro a.nametag {
|
||||||
|
color: #F00000;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.intro a {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
div.delete {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
div.post.reply p {
|
||||||
|
margin: 0.3em 0 0 0;
|
||||||
|
}
|
||||||
|
div.post.reply div.body {
|
||||||
|
margin-left: 1.8em;
|
||||||
|
margin-top: 0.8em;
|
||||||
|
padding-right: 3em;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
div.post.reply.highlighted {
|
||||||
|
background-color: #DCDCDC;
|
||||||
|
border-color: #999;
|
||||||
|
}
|
||||||
|
div.post.reply div.body a {
|
||||||
|
color: #D00;
|
||||||
|
}
|
||||||
|
div.post {
|
||||||
|
max-width: 97%;
|
||||||
|
}
|
||||||
|
div.post div.body {
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
div.post.reply {
|
||||||
|
background: #EEE;
|
||||||
|
margin: 0.2em 16px;
|
||||||
|
padding: 0.2em 0.3em 0.5em 0.6em;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: none solid solid none;
|
||||||
|
border-color: #DCDCDC;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
span.trip {
|
||||||
|
color: #228854;
|
||||||
|
}
|
||||||
|
span.quote {
|
||||||
|
color: #3C6D8A;
|
||||||
|
}
|
||||||
|
span.omitted {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
br.clear {
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
span.controls {
|
||||||
|
float: right;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
span.controls.op {
|
||||||
|
float: none;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
span.controls a {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
div#wrap {
|
||||||
|
width: 900px;
|
||||||
|
margin:0 auto;
|
||||||
|
}
|
||||||
|
div.ban {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #AF0A0F;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 30px auto;
|
||||||
|
}
|
||||||
|
div.ban p, div.ban h2 {
|
||||||
|
padding: 3px 7px;
|
||||||
|
}
|
||||||
|
div.ban h2 {
|
||||||
|
background: #AF0A0F;
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
div.ban p {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
div.ban p.reason {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
span.heading {
|
||||||
|
color: #000;
|
||||||
|
font-size: 11pt;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
span.spoiler {
|
||||||
|
background: black;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
div.post.reply div.body span.spoiler a {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
span.spoiler:hover, div.post.reply div.body span.spoiler:hover a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.styles {
|
||||||
|
float: right;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
div.styles a {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
div.styles a.selected {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
table.test {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
table.test td, table.test th {
|
||||||
|
text-align: left;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
table.test tr.h th {
|
||||||
|
background: #AF0A0F;
|
||||||
|
}
|
||||||
|
table.test td img {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
fieldset label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
div.pages {
|
||||||
|
color: #999;
|
||||||
|
background: #EEE;
|
||||||
|
display: inline;
|
||||||
|
padding: 8px;
|
||||||
|
border-right: 1px solid #DCDCDC;
|
||||||
|
border-bottom: 1px solid #DCDCDC;
|
||||||
|
}
|
||||||
|
div.pages a.selected {
|
||||||
|
color: black;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
div.pages a:link {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.pages form {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
div.pages form input {
|
||||||
|
margin: 0 5px;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid #DCDCDC;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
div.boardlist {
|
||||||
|
color: #999;
|
||||||
|
font-size: 9pt;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
div.boardlist.bottom {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
div.boardlist a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #AF0A0F;
|
||||||
|
}
|
||||||
|
div.report {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
table.modlog {
|
||||||
|
margin: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
table.modlog tr td {
|
||||||
|
text-align: left;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 4px 15px 0 0;
|
||||||
|
}
|
||||||
|
table.modlog tr th {
|
||||||
|
text-align: left;
|
||||||
|
padding: 4px 15px 5px 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
table.modlog tr th {
|
||||||
|
background: #AF0A0F;
|
||||||
|
}
|
||||||
|
td.minimal, th.minimal {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
div.top_notice {
|
||||||
|
text-align: center;
|
||||||
|
margin: 5px auto;
|
||||||
|
}
|
||||||
|
span.public_ban {
|
||||||
|
display: block;
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
span.toolong {
|
||||||
|
display: block;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
div.blotter {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-style div.boardlist:nth-child(1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-style div.boardlist:nth-child(1):hover, .desktop-style div.boardlist:nth-child(1).cb-menu {
|
||||||
|
background-color: rgba(80%, 80%, 80%, 0.35);
|
||||||
|
}
|
|
@ -8,6 +8,10 @@ body {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
a,a:visited {
|
a,a:visited {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
color: #34345C;
|
color: #34345C;
|
||||||
|
@ -194,6 +198,10 @@ img.banner,img.board_image {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.full-image {
|
||||||
|
max-width: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
div.post .post-image {
|
div.post .post-image {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
margin: 0 20px 0 0;
|
margin: 0 20px 0 0;
|
||||||
|
@ -414,8 +422,9 @@ fieldset label {
|
||||||
div.pages {
|
div.pages {
|
||||||
color: #89A;
|
color: #89A;
|
||||||
background: #D6DAF0;
|
background: #D6DAF0;
|
||||||
display: inline;
|
display: inline-block;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
margin: 8px 0 4px 0;
|
||||||
border-right: 1px solid #B7C5D9;
|
border-right: 1px solid #B7C5D9;
|
||||||
border-bottom: 1px solid #B7C5D9;
|
border-bottom: 1px solid #B7C5D9;
|
||||||
}
|
}
|
||||||
|
@ -463,20 +472,6 @@ hr {
|
||||||
clear: left;
|
clear: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.boardlist {
|
|
||||||
color: #89A;
|
|
||||||
font-size: 9pt;
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.boardlist.bottom {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.boardlist a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.report {
|
div.report {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
@ -661,7 +656,7 @@ pre {
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.thread:hover {
|
.theme-catalog div.thread:hover {
|
||||||
background: #D6DAF0;
|
background: #D6DAF0;
|
||||||
border-color: #B7C5D9;
|
border-color: #B7C5D9;
|
||||||
}
|
}
|
||||||
|
@ -824,10 +819,14 @@ div.thread:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
#options_div {
|
#options_div {
|
||||||
width: 600px;
|
width: 620px;
|
||||||
height: 320px;
|
height: 400px;
|
||||||
|
resize: both;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#alert_div {
|
#alert_div {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
|
@ -858,7 +857,7 @@ div.thread:hover {
|
||||||
#options_tablist {
|
#options_tablist {
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
width: 70px;
|
width: 90px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -887,8 +886,8 @@ div.thread:hover {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
bottom: 0px;
|
bottom: 10px;
|
||||||
left: 81px;
|
left: 101px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
@ -913,6 +912,18 @@ div.thread:hover {
|
||||||
|
|
||||||
.poster_id {
|
.poster_id {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-o-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.poster_id:hover {
|
||||||
|
color: #800000!important;
|
||||||
|
}
|
||||||
|
.poster_id::before {
|
||||||
|
content: " ID: ";
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
@ -940,6 +951,78 @@ span.pln {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clearfix {
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
font-size: 0px;
|
||||||
|
line-height: 0px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: none;
|
||||||
|
height: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === SPECIFIC PAGES & FEATURES === */
|
||||||
|
|
||||||
|
/* Board List */
|
||||||
|
div.boardlist {
|
||||||
|
margin-top: 3px;
|
||||||
|
|
||||||
|
color: #89A;
|
||||||
|
font-size: 9pt;
|
||||||
|
}
|
||||||
|
div.boardlist.bottom {
|
||||||
|
margin-top: 12px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
div.boardlist a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Threads */
|
||||||
|
/* Thread Footer */
|
||||||
|
#thread-interactions {
|
||||||
|
margin: 8px 0;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
#thread-links {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#thread-links > a {
|
||||||
|
padding-left: none;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
#thread-quick-reply {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
right: 50%;
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
margin-left: -50px;
|
||||||
|
}
|
||||||
|
#thread_stats {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#post-moderation-fields {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#delete-fields {
|
||||||
|
}
|
||||||
|
#report-fields {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* threadwatcher */
|
/* threadwatcher */
|
||||||
|
|
||||||
#watchlist {
|
#watchlist {
|
||||||
|
@ -1115,3 +1198,11 @@ table.fileboard .intro a {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
.own_post {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
opacity: .666;
|
||||||
|
}
|
||||||
|
div.mix {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<title>{{ board.url }} - {{ board.name }}</title>
|
<title>{{ board.url }} - {{ board.name }}</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
|
||||||
{{ boardlist.top }}
|
{{ boardlist.top }}
|
||||||
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %}
|
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %}
|
||||||
{% if config.url_banner %}<img class="banner" src="{{ config.url_banner }}" {% if config.banner_width or config.banner_height %}style="{% if config.banner_width %}width:{{ config.banner_width }}px{% endif %};{% if config.banner_width %}height:{{ config.banner_height }}px{% endif %}" {% endif %}alt="" />{% endif %}
|
{% if config.url_banner %}<img class="banner" src="{{ config.url_banner }}" {% if config.banner_width or config.banner_height %}style="{% if config.banner_width %}width:{{ config.banner_width }}px{% endif %};{% if config.banner_width %}height:{{ config.banner_height }}px{% endif %}" {% endif %}alt="" />{% endif %}
|
||||||
|
@ -36,10 +36,10 @@
|
||||||
{% endfor %} {{ btn.next }}</div>
|
{% endfor %} {{ btn.next }}</div>
|
||||||
{{ boardlist.bottom }}
|
{{ boardlist.bottom }}
|
||||||
<footer>
|
<footer>
|
||||||
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
|
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
|
||||||
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
|
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
|
||||||
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright © 2010-2014 Tinyboard Development Group
|
<br>Tinyboard Copyright © 2010-2014 Tinyboard Development Group
|
||||||
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2015 vichan-devel</p>
|
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2016 vichan-devel</p>
|
||||||
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %}
|
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %}
|
||||||
</footer>
|
</footer>
|
||||||
<script type="text/javascript">{% raw %}
|
<script type="text/javascript">{% raw %}
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
{% for javascript in config.additional_javascript %}<script type="text/javascript" src="{{ config.additional_javascript_url }}{{ javascript }}"></script>{% endfor %}
|
{% for javascript in config.additional_javascript %}<script type="text/javascript" src="{{ config.additional_javascript_url }}{{ javascript }}"></script>{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.recaptcha %}<style type="text/css">{% raw %}
|
{% if config.recaptcha %}<script src="//www.google.com/recaptcha/api.js"></script>
|
||||||
|
<style type="text/css">{% raw %}
|
||||||
#recaptcha_area {
|
#recaptcha_area {
|
||||||
float: none !important;
|
float: none !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
|
|
@ -4,17 +4,19 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
var
|
||||||
{% if not no_post_form %}
|
{% if not no_post_form %}
|
||||||
var active_page = "index";
|
active_page = "index"
|
||||||
|
, board_name = "{{ board.uri }}";
|
||||||
{% else %}
|
{% else %}
|
||||||
var active_page = "ukko";
|
active_page = "ukko";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
<title>{{ board.url }} - {{ board.title|e }}</title>
|
<title>{{ board.url }} - {{ board.title|e }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} active-{% if not no_post_form %}index{% else %}ukko{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
|
||||||
{{ boardlist.top }}
|
{{ boardlist.top }}
|
||||||
|
|
||||||
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %}
|
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %}
|
||||||
|
@ -55,6 +57,17 @@
|
||||||
|
|
||||||
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
|
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
|
||||||
<hr />
|
<hr />
|
||||||
|
<!-- Start Search Form -->
|
||||||
|
{% if config.board_search %}
|
||||||
|
<form style="display:inline" action="/search.php">
|
||||||
|
<p style="margin: 10px;">
|
||||||
|
<input type="text" name="search" placeholder="{{ board.uri }} search">
|
||||||
|
<input type="hidden" name="board" value="{{ board.uri }}">
|
||||||
|
<input type="submit" value="Search">
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
<!-- End Search Form -->
|
||||||
<form name="postcontrols" action="{{ config.post_url }}" method="post">
|
<form name="postcontrols" action="{{ config.post_url }}" method="post">
|
||||||
<input type="hidden" name="board" value="{{ board.uri }}" />
|
<input type="hidden" name="board" value="{{ board.uri }}" />
|
||||||
{% if mod %}<input type="hidden" name="mod" value="1" />{% endif %}
|
{% if mod %}<input type="hidden" name="mod" value="1" />{% endif %}
|
||||||
|
@ -67,7 +80,7 @@
|
||||||
[<a {% if page.selected %}class="selected"{% endif %}{% if not page.selected %}href="{{ page.link }}"{% endif %}>{{ page.num }}</a>]{% if loop.last %} {% endif %}
|
[<a {% if page.selected %}class="selected"{% endif %}{% if not page.selected %}href="{{ page.link }}"{% endif %}>{{ page.num }}</a>]{% if loop.last %} {% endif %}
|
||||||
{% endfor %} {{ btn.next }}
|
{% endfor %} {{ btn.next }}
|
||||||
{% if config.catalog_link %}
|
{% if config.catalog_link %}
|
||||||
| <a href="{{ config.root }}{{ board.dir }}{{ config.catalog_link }}">Catalog</a>
|
| <a href="{{ config.root }}{{ board.dir }}{{ config.catalog_link }}">{% trans %}Catalog{% endtrans %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -76,10 +89,10 @@
|
||||||
{{ config.ad.bottom }}
|
{{ config.ad.bottom }}
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
|
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
|
||||||
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
|
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
|
||||||
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright © 2010-2014 Tinyboard Development Group
|
<br>Tinyboard Copyright © 2010-2014 Tinyboard Development Group
|
||||||
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2015 vichan-devel</p>
|
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2016 vichan-devel</p>
|
||||||
|
|
||||||
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %}
|
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %}
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -87,6 +87,9 @@
|
||||||
<legend>Miscellaneous</legend>
|
<legend>Miscellaneous</legend>
|
||||||
<label for="secure_trip_salt">Secure trip (##) salt:</label>
|
<label for="secure_trip_salt">Secure trip (##) salt:</label>
|
||||||
<input type="text" id="secure_trip_salt" name="secure_trip_salt" value="{{ config.secure_trip_salt }}" size="40">
|
<input type="text" id="secure_trip_salt" name="secure_trip_salt" value="{{ config.secure_trip_salt }}" size="40">
|
||||||
|
|
||||||
|
<label for="more">Additional configuration:</label>
|
||||||
|
<textarea id="more" name="more">{{ more }}</textarea>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<p style="text-align:center">
|
<p style="text-align:center">
|
||||||
|
|
|
@ -70,8 +70,8 @@ var datelocale =
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function alert(a) {
|
function alert(a, do_confirm, confirm_ok_action, confirm_cancel_action) {
|
||||||
var handler, div;
|
var handler, div, bg, closebtn, okbtn;
|
||||||
var close = function() {
|
var close = function() {
|
||||||
handler.fadeOut(400, function() { handler.remove(); });
|
handler.fadeOut(400, function() { handler.remove(); });
|
||||||
return false;
|
return false;
|
||||||
|
@ -79,15 +79,29 @@ function alert(a) {
|
||||||
|
|
||||||
handler = $("<div id='alert_handler'></div>").hide().appendTo('body');
|
handler = $("<div id='alert_handler'></div>").hide().appendTo('body');
|
||||||
|
|
||||||
$("<div id='alert_background'></div>").click(close).appendTo(handler);
|
bg = $("<div id='alert_background'></div>").appendTo(handler);
|
||||||
|
|
||||||
div = $("<div id='alert_div'></div>").appendTo(handler);
|
div = $("<div id='alert_div'></div>").appendTo(handler);
|
||||||
$("<a id='alert_close' href='javascript:void(0)'><i class='fa fa-times'></i></div>")
|
closebtn = $("<a id='alert_close' href='javascript:void(0)'><i class='fa fa-times'></i></div>")
|
||||||
.click(close).appendTo(div);
|
.appendTo(div);
|
||||||
|
|
||||||
$("<div id='alert_message'></div>").html(a).appendTo(div);
|
$("<div id='alert_message'></div>").html(a).appendTo(div);
|
||||||
|
|
||||||
$("<button class='button alert_button'>"+_("OK")+"</button>").click(close).appendTo(div);
|
okbtn = $("<button class='button alert_button'>"+_("OK")+"</button>").appendTo(div);
|
||||||
|
|
||||||
|
if (do_confirm) {
|
||||||
|
confirm_ok_action = (typeof confirm_ok_action !== "function") ? function(){} : confirm_ok_action;
|
||||||
|
confirm_cancel_action = (typeof confirm_cancel_action !== "function") ? function(){} : confirm_cancel_action;
|
||||||
|
okbtn.click(confirm_ok_action);
|
||||||
|
$("<button class='button alert_button'>"+_("Cancel")+"</button>").click(confirm_cancel_action).click(close).appendTo(div);
|
||||||
|
bg.click(confirm_cancel_action);
|
||||||
|
okbtn.click(confirm_cancel_action);
|
||||||
|
closebtn.click(confirm_cancel_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
bg.click(close);
|
||||||
|
okbtn.click(close);
|
||||||
|
closebtn.click(close);
|
||||||
|
|
||||||
handler.fadeIn(400);
|
handler.fadeIn(400);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +115,10 @@ var styles = {
|
||||||
{% for stylesheet in stylesheets %}{% raw %}'{% endraw %}{{ stylesheet.name|addslashes }}{% raw %}' : '{% endraw %}{{ stylesheet.uri|addslashes }}{% raw %}',
|
{% for stylesheet in stylesheets %}{% raw %}'{% endraw %}{{ stylesheet.name|addslashes }}{% raw %}' : '{% endraw %}{{ stylesheet.uri|addslashes }}{% raw %}',
|
||||||
{% endraw %}{% endfor %}{% raw %}
|
{% endraw %}{% endfor %}{% raw %}
|
||||||
};
|
};
|
||||||
var board_name = false;
|
|
||||||
|
if (typeof board_name === 'undefined') {
|
||||||
|
var board_name = false;
|
||||||
|
}
|
||||||
|
|
||||||
function changeStyle(styleName, link) {
|
function changeStyle(styleName, link) {
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
@ -145,13 +162,7 @@ function changeStyle(styleName, link) {
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
{% if config.stylesheets_board %}
|
{% if config.stylesheets_board %}
|
||||||
{# This is such an unacceptable mess. There needs to be an easier way. #}
|
|
||||||
{# Needs fix for slugify #}
|
|
||||||
var matches = document.URL.match(/\/(\w+)\/($|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.file_index|replace({'.': '\\.'}) }}|{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }})/);
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
if (matches) {
|
|
||||||
board_name = matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!localStorage.board_stylesheets) {
|
if (!localStorage.board_stylesheets) {
|
||||||
localStorage.board_stylesheets = '{}';
|
localStorage.board_stylesheets = '{}';
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
{% if not hide_ip %}
|
{% if not hide_ip %}
|
||||||
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip }}">
|
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip|e }}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<em>{% trans 'hidden' %}</em>
|
<em>{% trans 'hidden' %}</em>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
{% if mod|hasPermission(config.mod.manageboards) %}
|
{% if mod|hasPermission(config.mod.manageboards) %}
|
||||||
<a href="?/edit/{{ board.uri }}"><small>[{% trans 'edit' %}]</small></a>
|
<a href="?/edit/{{ board.uri }}"><small>[{% trans 'edit' %}]</small></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if mod|hasPermission(config.mod.edit_pages) %}
|
||||||
|
<a href="?/edit_pages/{{ board.uri }}"><small>[{% trans 'pages' %}]</small></a>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
@ -62,7 +65,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="?/noticeboard">{% trans 'View all noticeboard entries' %}</a></li>
|
<li><a href="?/noticeboard">{% trans 'View all noticeboard entries' %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="?/news">{% trans 'News' %}</a></li>
|
<li><a href="?/edit_news">{% trans 'News' %}</a></li>
|
||||||
<li>
|
<li>
|
||||||
<a href="?/inbox">
|
<a href="?/inbox">
|
||||||
{% trans 'PM inbox' %}
|
{% trans 'PM inbox' %}
|
||||||
|
@ -100,6 +103,9 @@
|
||||||
{% if mod|hasPermission(config.mod.modlog) %}
|
{% if mod|hasPermission(config.mod.modlog) %}
|
||||||
<li><a href="?/log">{% trans 'Moderation log' %}</a></li>
|
<li><a href="?/log">{% trans 'Moderation log' %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if mod|hasPermission(config.mod.edit_pages) %}
|
||||||
|
<li><a href="?/edit_pages">{% trans 'Global static pages' %}</a></li>
|
||||||
|
{% endif %}
|
||||||
{% if mod|hasPermission(config.mod.recent) %}
|
{% if mod|hasPermission(config.mod.recent) %}
|
||||||
<li><a href="?/recent/25">{% trans 'Recent posts' %}</a></li>
|
<li><a href="?/recent/25">{% trans 'Recent posts' %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
29
templates/mod/edit_page.html
Normal file
29
templates/mod/edit_page.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<div style="text-align:center">
|
||||||
|
<form method="POST">
|
||||||
|
<input name="token" value="{{ token }}" type="hidden">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans %}Markup method{% endtrans %}
|
||||||
|
{% set allowed_html = config.allowed_html %}
|
||||||
|
{% trans %}<p class="unimportant">"markdown" is provided by <a href="http://parsedown.org/">parsedown</a>. Note: images disabled.</p>
|
||||||
|
<p class="unimportant">"html" allows the following tags:<br/>{{ allowed_html }}</p>
|
||||||
|
<p class="unimportant">"infinity" is the same as what is used in posts.</p>
|
||||||
|
<p class="unimportant">This page will not convert between formats,<br/>choose it once or do the conversion yourself!</p>{% endtrans %}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<select name="method">
|
||||||
|
{% for markup in ['markdown', 'html', 'infinity'] %}
|
||||||
|
<option value="{{ markup }}" {% if page.type == markup %}selected{% endif %}>{{ markup }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td></tr>
|
||||||
|
<tr><th>{% trans %}Page content{% endtrans %}
|
||||||
|
<br/>
|
||||||
|
<span class="unimportant">{% trans %}Page will appear at:{% endtrans %}
|
||||||
|
{% if board %} <a href="/{{ board.uri }}/{{ page.name }}.html">{{ config.domain }}/{{ board.uri }}/{{ page.name }}.html</a>
|
||||||
|
{% else %} <a href="/{{ page.name }}.html">{{ config.site }}/{{ page.name }}.html</a>
|
||||||
|
{% endif %}</span></th><td><textarea name="content" style="height:500px;width:500px">{{content}}</textarea></td><tr>
|
||||||
|
</table>
|
||||||
|
<input type="submit" value="Save page">
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -7,7 +7,7 @@
|
||||||
{% trans %}Name{% endtrans %}
|
{% trans %}Name{% endtrans %}
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="name" size="25" maxlength="35" autocomplete="off" value="{{ post.name }}">
|
<input type="text" name="name" size="25" maxlength="35" autocomplete="off" value="{{ post.name|e }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
{% trans %}Subject{% endtrans %}
|
{% trans %}Subject{% endtrans %}
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<input style="float:left;" type="text" name="subject" size="25" maxlength="100" autocomplete="off" value="{{ post.subject }}">
|
<input style="float:left;" type="text" name="subject" size="25" maxlength="100" autocomplete="off" value="{{ post.subject|e }}">
|
||||||
<input accesskey="s" style="margin-left:2px;" type="submit" name="post" value="{% trans %}Update{% endtrans %}">
|
<input accesskey="s" style="margin-left:2px;" type="submit" name="post" value="{% trans %}Update{% endtrans %}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -10,7 +10,15 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="minimal">
|
<td class="minimal">
|
||||||
{% if log.username %}
|
{% if log.username %}
|
||||||
|
{% if hide_names %}
|
||||||
|
<em>hidden</em>
|
||||||
|
{% else %}
|
||||||
|
{% if not mod|hasPermission(config.mod.modlog) %}
|
||||||
|
<a href="?/new_PM/{{ log.username|e }}">{{ log.username|e }}</a>
|
||||||
|
{% else %}
|
||||||
<a href="?/log:{{ log.username|e }}">{{ log.username|e }}</a>
|
<a href="?/log:{{ log.username|e }}">{{ log.username|e }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% elseif log.mod == -1 %}
|
{% elseif log.mod == -1 %}
|
||||||
<em>system</em>
|
<em>system</em>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -44,7 +52,11 @@
|
||||||
{% if count > logs|count %}
|
{% if count > logs|count %}
|
||||||
<p class="unimportant" style="text-align:center;word-wrap:break-word">
|
<p class="unimportant" style="text-align:center;word-wrap:break-word">
|
||||||
{% for i in range(0, (count - 1) / config.mod.modlog_page) %}
|
{% for i in range(0, (count - 1) / config.mod.modlog_page) %}
|
||||||
<a href="?/log{% if username %}:{{ username }}{% endif %}/{{ i + 1 }}">[{{ i + 1 }}]</a>
|
{% if public %}
|
||||||
|
<a href="?page={{ i + 1 }}&board={{ board|url_encode }}">[{{ i + 1 }}]</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="?/log{% if username %}:{{ username }}{% elseif board %}:b:{{ board }}{% endif %}/{{ i + 1 }}">[{{ i + 1 }}]</a>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<div class="ban">
|
<div class="ban">
|
||||||
{% if mod|hasPermission(config.mod.news_delete) %}
|
{% if mod|hasPermission(config.mod.news_delete) %}
|
||||||
<span style="float:right;padding:2px">
|
<span style="float:right;padding:2px">
|
||||||
<a class="unimportant" href="?/news/delete/{{ post.id }}/{{ post.delete_token }}">[{% trans 'delete' %}]</a>
|
<a class="unimportant" href="?/edit_news/delete/{{ post.id }}/{{ post.delete_token }}">[{% trans 'delete' %}]</a>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2 id="{{ post.id }}">
|
<h2 id="{{ post.id }}">
|
||||||
|
|
34
templates/mod/pages.html
Normal file
34
templates/mod/pages.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<script type="text/javascript" src="js/jquery.min.js"></script>
|
||||||
|
<div style="text-align:center">
|
||||||
|
<p class="unimportant">
|
||||||
|
{% if board %}
|
||||||
|
{% set page_max = config.pages_max %}
|
||||||
|
{% trans %}This page allows you to create static pages for your board. The limit is {{ page_max }} pages per board. You will still have to link to your pages somewhere in your board, for example in a sticky or in the board's announcement. To make links in the board's announcement, use <a> HTML tags.{% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
{% trans %}This page allows you to create static pages for your imageboard.{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
|
<h2>{% trans %}Existing pages{% endtrans %}</h2>
|
||||||
|
{% if pages %}
|
||||||
|
<form>
|
||||||
|
<table style="margin:auto">
|
||||||
|
<tr><th>{% trans %}URL{% endtrans %}</th><th>{% trans %}Title{% endtrans %}</th><th>{% trans %}Edit{% endtrans %}</th><th>{% trans %}Delete{% endtrans %}</tr>
|
||||||
|
{% for page in pages %}
|
||||||
|
<tr><td>{{ page.name }}</td><td>{{ page.title }}</td><td><a href="?/edit_page/{{ page.id }}">{% trans %}Edit{% endtrans %}</a></td><td><a href="?/edit_pages/delete/{{ page.name }}{% if board %}/{{ board }}{% endif %}/{{ page.delete_token }}">{% trans %}Delete{% endtrans %}</a></td>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<em>No pages yet!</em>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
<hr/>
|
||||||
|
<h2>{% trans %}Create a new page{% endtrans %}</h2>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}">
|
||||||
|
<table>
|
||||||
|
<tr><th>{% trans %}URL{% endtrans %}</th><th>{% trans %}Title{% endtrans %}</th></tr>
|
||||||
|
<tr><td><input type="text" name="page"></td><td><input type="text" name="title"></td>
|
||||||
|
</table>
|
||||||
|
<input type="submit" value="{% trans %}Create{% endtrans %}">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
|
@ -1,12 +1,12 @@
|
||||||
<div class="ban">
|
<div class="ban">
|
||||||
<h2>{% trans 'Rebuilt' %}</h2>
|
<h2>{% trans 'Rebuilt' %}</h2>
|
||||||
|
<p>
|
||||||
|
<a href="?/rebuild">{% trans 'Go back and rebuild again' %}</a>.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for log in logs %}
|
{% for log in logs %}
|
||||||
<li>{{ log }}</li>
|
<li>{{ log }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
|
||||||
<a href="?/rebuild">{% trans 'Go back and rebuild again' %}</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@
|
||||||
<a class="email" href="mailto:{{ post.email }}">
|
<a class="email" href="mailto:{{ post.email }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% set capcode = post.capcode|capcode %}
|
{% 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|e }}</span>
|
||||||
{% if post.trip|length > 0 %}
|
{% if post.trip|length > 0 %}
|
||||||
<span {% if capcode.trip %}style="{{ capcode.trip }}" {% endif %}class="trip">{{ post.trip }}</span>
|
<span {% if capcode.trip %}style="{{ capcode.trip }}" {% endif %}class="trip">{{ post.trip }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -239,7 +239,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td style="max-width:250px">
|
<td style="max-width:250px">
|
||||||
{% if post.subject %}
|
{% if post.subject %}
|
||||||
<small>{{ post.subject }}</small>
|
<small>{{ post.subject|e }}</small>
|
||||||
{% else %}
|
{% else %}
|
||||||
–
|
–
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</td>
|
</td>
|
||||||
{% if mod|hasPermission(config.mod.remove_notes) %}
|
{% if mod|hasPermission(config.mod.remove_notes) %}
|
||||||
<td class="minimal">
|
<td class="minimal">
|
||||||
<a href="?/IP/{{ ip }}/remove_note/{{ note.id }}">
|
<a href="?/IP/{{ ip|url_encode(true) }}/remove_note/{{ note.id }}">
|
||||||
<small>[{% trans 'remove' %}]</small>
|
<small>[{% trans 'remove' %}]</small>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
active_page = "page";
|
var active_page = "page";
|
||||||
</script>
|
</script>
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} active-page" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
|
||||||
|
{{ boardlist.top }}
|
||||||
|
|
||||||
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr>{% endif %}
|
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr>{% endif %}
|
||||||
<header>
|
<header>
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
|
@ -22,10 +24,10 @@
|
||||||
{{ body }}
|
{{ body }}
|
||||||
<hr>
|
<hr>
|
||||||
<footer>
|
<footer>
|
||||||
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
|
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
|
||||||
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
|
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
|
||||||
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright © 2010-2014 Tinyboard Development Group
|
<br>Tinyboard Copyright © 2010-2014 Tinyboard Development Group
|
||||||
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2015 vichan-devel</p>
|
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2016 vichan-devel</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<a href="http://imgops.com/{{ config.domain }}{{ config.uri_img }}{{ file.file }}" target="_blank">ImgOps</a>
|
<a href="http://imgops.com/{{ config.domain }}{{ config.uri_img }}{{ file.file }}" target="_blank">ImgOps</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.image_identification_exif and file.file|extension == 'jpg' %}
|
{% if config.image_identification_exif and file.file|extension == 'jpg' %}
|
||||||
<a href="http://regex.info/exif.cgi?url={{ config.domain }}{{ config.uri_img }}{{ file.file }}" target="_blank">Exif</a>
|
<a href="http://exif.int21h.win/?url={{ config.domain }}{{ config.uri_img }}{{ file.file }}" target="_blank">Exif</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.image_identification_google %}
|
{% if config.image_identification_google %}
|
||||||
<a href="https://www.google.com/searchbyimage?image_url={{ config.domain|url_encode }}{{ config.uri_img|url_encode }}{{ file.file|url_encode }}" target="_blank">Google</a>
|
<a href="https://www.google.com/searchbyimage?image_url={{ config.domain|url_encode }}{{ config.uri_img|url_encode }}{{ file.file|url_encode }}" target="_blank">Google</a>
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if mod|hasPermission(config.mod.move, board.uri) %}
|
{% if mod|hasPermission(config.mod.move, board.uri) %}
|
||||||
{% if not post.thread %}
|
{% if not post.thread %}
|
||||||
<a title="{% trans %}Move thread to another board{% endtrans %}" href="?/{{ board.dir }}move/{{ post.id }}">{{ config.mod.link_move }}</a>
|
<a title="{% trans %}Move thread to another board{% endtrans %}" href="?/{{ board.dir }}move/{{ post.id }}">{{ config.mod.link_move }}</a>
|
||||||
|
@ -49,6 +48,13 @@
|
||||||
<a title="{% trans %}Move reply to another board{% endtrans %}" href="?/{{ board.dir }}move_reply/{{ post.id }}">{{ config.mod.link_move }}</a>
|
<a title="{% trans %}Move reply to another board{% endtrans %}" href="?/{{ board.dir }}move_reply/{{ post.id }}">{{ config.mod.link_move }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if mod|hasPermission(config.mod.cycle, board.uri) %}
|
||||||
|
{% if post.cycle %}
|
||||||
|
<a title="{% trans %}Make thread not cycle{% endtrans %}" href="?/{{ secure_link(board.dir ~ 'uncycle/' ~ post.id) }}">{{ config.mod.link_uncycle }}</a>
|
||||||
|
{% else %}
|
||||||
|
<a title="{% trans %}Make thread cycle{% endtrans %}" href="?/{{ secure_link(board.dir ~ 'cycle/' ~ post.id) }}">{{ config.mod.link_cycle }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% if mod|hasPermission(config.mod.editpost, board.uri) %}
|
{% if mod|hasPermission(config.mod.editpost, board.uri) %}
|
||||||
<a title="{% trans %}Edit post{% endtrans %}" href="?/{{ board.dir }}edit{% if config.mod.raw_html_default %}_raw{% endif %}/{{ post.id }}">{{ config.mod.link_editpost }}</a>
|
<a title="{% trans %}Edit post{% endtrans %}" href="?/{{ board.dir }}edit{% if config.mod.raw_html_default %}_raw{% endif %}/{{ post.id }}">{{ config.mod.link_editpost }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% if config.poster_ids %}
|
{% if config.poster_ids %}
|
||||||
{% if post.thread %}
|
{% if post.thread %}
|
||||||
ID: <span class="poster_id">{{ post.ip|poster_id(post.thread) }}</span>
|
<span class="poster_id">{{ post.ip|poster_id(post.thread) }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
ID: <span class="poster_id">{{ post.ip|poster_id(post.id) }}</span>
|
<span class="poster_id">{{ post.ip|poster_id(post.id) }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -74,11 +74,32 @@
|
||||||
{{ antibot.html() }}
|
{{ antibot.html() }}
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<script type="text/javascript" src="//www.google.com/recaptcha/api/challenge?k={{ config.recaptcha_public }}"></script>
|
<div class="g-recaptcha" data-sitekey="{{ config.recaptcha_public }}"></div>
|
||||||
{{ antibot.html() }}
|
{{ antibot.html() }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if config.captcha.enabled %}
|
||||||
|
<tr class='captcha'>
|
||||||
|
<th>
|
||||||
|
{% trans %}Verification{% endtrans %}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<script>load_captcha("{{ config.captcha.provider_get }}", "{{ config.captcha.extra }}");</script>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% elseif config.new_thread_capt %}
|
||||||
|
{% if not id %}
|
||||||
|
<tr class='captcha'>
|
||||||
|
<th>
|
||||||
|
{% trans %}Verification{% endtrans %}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<script>load_captcha("{{ config.captcha.provider_get }}", "{{ config.captcha.extra }}");</script>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% if config.user_flag %}
|
{% if config.user_flag %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans %}Flag{% endtrans %}</th>
|
<th>{% trans %}Flag{% endtrans %}</th>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% filter remove_whitespace %}
|
{% filter remove_whitespace %}
|
||||||
{# tabs and new lines will be ignored #}
|
{# tabs and new lines will be ignored #}
|
||||||
|
|
||||||
<div id="thread_{{ post.id }}" data-board="{{ board.uri }}">
|
<div class="thread" id="thread_{{ post.id }}" data-board="{{ board.uri }}">
|
||||||
{% if not index %}<a id="{{ post.id }}" class="post_anchor"></a>{% endif %}
|
{% if not index %}<a id="{{ post.id }}" class="post_anchor"></a>{% endif %}
|
||||||
|
|
||||||
{% include 'post/fileinfo.html' %}
|
{% include 'post/fileinfo.html' %}
|
||||||
|
@ -19,25 +19,32 @@
|
||||||
<a class="post_no" onclick="citeReply({{ post.id }})" href="{% if isnoko50 %}{{ post.link('q', '50') }}{% else %}{{ post.link('q') }}{% endif %}">{{ post.id }}</a>
|
<a class="post_no" onclick="citeReply({{ post.id }})" href="{% if isnoko50 %}{{ post.link('q', '50') }}{% else %}{{ post.link('q') }}{% endif %}">{{ post.id }}</a>
|
||||||
{% if post.sticky %}
|
{% if post.sticky %}
|
||||||
{% if config.font_awesome %}
|
{% if config.font_awesome %}
|
||||||
<i class="fa fa-thumb-tack"></i>
|
<i class="fa fa-thumb-tack" title="Sticky"></i>
|
||||||
{% else %}
|
{% else %}
|
||||||
<img class="icon" title="Sticky" src="{{ config.image_sticky }}" alt="Sticky" />
|
<img class="icon" title="Sticky" src="{{ config.image_sticky }}" alt="Sticky" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if post.locked %}
|
{% if post.locked %}
|
||||||
{% if config.font_awesome %}
|
{% if config.font_awesome %}
|
||||||
<i class="fa fa-lock"></i>
|
<i class="fa fa-lock" title="Locked"></i>
|
||||||
{% else %}
|
{% else %}
|
||||||
<img class="icon" title="Locked" src="{{ config.image_locked }}" alt="Locked" />
|
<img class="icon" title="Locked" src="{{ config.image_locked }}" alt="Locked" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if post.bumplocked and (config.mod.view_bumplock < 0 or (post.mod and post.mod|hasPermission(config.mod.view_bumplock, board.uri))) %}
|
{% if post.bumplocked and (config.mod.view_bumplock < 0 or (post.mod and post.mod|hasPermission(config.mod.view_bumplock, board.uri))) %}
|
||||||
{% if config.font_awesome %}
|
{% if config.font_awesome %}
|
||||||
<i class="fa fa-anchor"></i>
|
<i class="fa fa-anchor" title="Bumplocked"></i>
|
||||||
{% else %}
|
{% else %}
|
||||||
<img class="icon" title="Bumplocked" src="{{ config.image_bumplocked }}" alt="Bumplocked" />
|
<img class="icon" title="Bumplocked" src="{{ config.image_bumplocked }}" alt="Bumplocked" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if post.cycle %}
|
||||||
|
{% if config.font_awesome %}
|
||||||
|
<i class="fa fa-refresh" title="Cyclical"></i>
|
||||||
|
{% else %}
|
||||||
|
<img class="icon" title="Cyclical" src="{{ config.image_sticky }}" alt="Cyclical" />
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% if index %}
|
{% if index %}
|
||||||
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post) }}">[{% trans %}Reply{% endtrans %}]</a>
|
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post) }}">[{% trans %}Reply{% endtrans %}]</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS ``posts_{{ board }}`` (
|
||||||
`ip` varchar(39) CHARACTER SET ascii NOT NULL,
|
`ip` varchar(39) CHARACTER SET ascii NOT NULL,
|
||||||
`sticky` int(1) NOT NULL,
|
`sticky` int(1) NOT NULL,
|
||||||
`locked` int(1) NOT NULL,
|
`locked` int(1) NOT NULL,
|
||||||
|
`cycle` int(1) NOT NULL,
|
||||||
`sage` int(1) NOT NULL,
|
`sage` int(1) NOT NULL,
|
||||||
`embed` text,
|
`embed` text,
|
||||||
`slug` varchar(256) DEFAULT NULL,
|
`slug` varchar(256) DEFAULT NULL,
|
||||||
|
|
22
templates/report.html
Normal file
22
templates/report.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<form action="{{ config.post_url }}" method="post" id="report_form">
|
||||||
|
{% if error %}
|
||||||
|
<div class="error">
|
||||||
|
<strong>{{ error|e }}</strong>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<input type="hidden" name="board" value="{{ board.uri }}">
|
||||||
|
<input type="hidden" name="{{ post|e }}" value="1">
|
||||||
|
{% if global %}
|
||||||
|
<input type="hidden" name="global" value="1">
|
||||||
|
<div><h1>Attention!</h1><p>This form is only for reporting <strong>child pornography</strong>, <strong>bot spam</strong> and <strong>credit card numbers, social security numbers or banking information</strong>. DMCA requests and all other deletion requests <em>MUST</em> be sent via email to admin@8chan.co.</p><p>8chan is unmoderated and allows posts without collecting <em>ANY</em> information from the poster less the details of their post. Furthermore, all boards on 8chan are user created and not actively monitored by anyone but the board creator.</p><p>8chan has a small volunteer staff to handle this queue, please do not waste their time by filling it with nonsense! <em>If you made a report with this tool and the post was not deleted, <strong>do not make the report again!</strong> Email admin@8chan.co instead.</em> Abuse of the global report system could lead to address blocks against your IP from 8chan.</p><p>Again, 8chan's global volunteers <em>do not</em> handle board specific issues. You most likely want to click "Report" instead to reach the creator and volunteers he assigned to this board.</p>
|
||||||
|
{% endif %}
|
||||||
|
<p>{% trans %}Enter reason below...{% endtrans %}</p>
|
||||||
|
<input type="text" id="reason" name="reason" value="{{ reason_prefill|e|addslashes }}">
|
||||||
|
{% if config.report_captcha %}
|
||||||
|
<p>{% trans %}To submit your report, please fill out the CAPTCHA below.{% endtrans %}</p>
|
||||||
|
{{ captcha['html'] }}<br/>
|
||||||
|
<input class="captcha_text" name="captcha_text" size="25" maxlength="6" autocomplete="off" type="text" value="">
|
||||||
|
<input class="captcha_cookie" name="captcha_cookie" type="hidden" autocomplete="off" value="{{ captcha['cookie']|e }}"><br/>
|
||||||
|
{% endif %}
|
||||||
|
<input name="report" value="{% trans %}Submit{% endtrans %}" type="submit">
|
||||||
|
</form>
|
|
@ -1,13 +1,16 @@
|
||||||
{% if config.allow_delete %}
|
<div id="post-moderation-fields">
|
||||||
<div class="delete">
|
{% if config.allow_delete %}
|
||||||
|
<div id="delete-fields">
|
||||||
{% trans %}Delete Post{% endtrans %} [<input title="Delete file only" type="checkbox" name="file" id="delete_file" />
|
{% trans %}Delete Post{% endtrans %} [<input title="Delete file only" type="checkbox" name="file" id="delete_file" />
|
||||||
<label for="delete_file">{% trans %}File{% endtrans %}</label>] <label for="password">{% trans %}Password{% endtrans %}</label>
|
<label for="delete_file">{% trans %}File{% endtrans %}</label>] <label for="password">{% trans %}Password{% endtrans %}</label>
|
||||||
<input id="password" type="password" name="password" size="12" maxlength="18" />
|
<input id="password" type="password" name="password" size="12" maxlength="18" />
|
||||||
<input type="submit" name="delete" value="{% trans %}Delete{% endtrans %}" />
|
<input type="submit" name="delete" value="{% trans %}Delete{% endtrans %}" />
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="delete" style="clear:both">
|
|
||||||
|
<div id="report-fields">
|
||||||
<label for="reason">{% trans %}Reason{% endtrans %}</label>
|
<label for="reason">{% trans %}Reason{% endtrans %}</label>
|
||||||
<input id="reason" type="text" name="reason" size="20" maxlength="30" />
|
<input id="reason" type="text" name="reason" size="20" maxlength="30" />
|
||||||
<input type="submit" name="report" value="{% trans %}Report{% endtrans %}" />
|
<input type="submit" name="report" value="{% trans %}Report{% endtrans %}" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -11,7 +11,7 @@
|
||||||
{% if config.default_stylesheet.1 != '' %}<link rel="stylesheet" type="text/css" id="stylesheet" href="{{ config.uri_stylesheets }}{{ config.default_stylesheet.1 }}">{% endif %}
|
{% if config.default_stylesheet.1 != '' %}<link rel="stylesheet" type="text/css" id="stylesheet" href="{{ config.uri_stylesheets }}{{ config.default_stylesheet.1 }}">{% endif %}
|
||||||
{% if config.font_awesome %}<link rel="stylesheet" href="{{ config.root }}{{ config.font_awesome_css }}">{% endif %}
|
{% if config.font_awesome %}<link rel="stylesheet" href="{{ config.root }}{{ config.font_awesome_css }}">{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
|
||||||
{{ boardlist.top }}
|
{{ boardlist.top }}
|
||||||
<header>
|
<header>
|
||||||
<h1>{{ settings.title }}</h1>
|
<h1>{{ settings.title }}</h1>
|
||||||
|
@ -38,10 +38,10 @@
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<footer>
|
<footer>
|
||||||
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
|
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
|
||||||
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
|
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
|
||||||
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright © 2010-2014 Tinyboard Development Group
|
<br>Tinyboard Copyright © 2010-2014 Tinyboard Development Group
|
||||||
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2015 vichan-devel</p>
|
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2016 vichan-devel</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -4,31 +4,25 @@
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
<script type='text/javascript'>
|
<script type='text/javascript'>
|
||||||
active_page = "catalog";
|
var active_page = "catalog"
|
||||||
|
, board_name = "{{ board }}";
|
||||||
</script>
|
</script>
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
<title>{{ settings.title }}</title>
|
<title>{{ board }} - Catalog</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="theme-catalog">
|
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} theme-catalog active-catalog" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
|
||||||
{{ boardlist.top }}
|
{{ boardlist.top }}
|
||||||
<header>
|
<header>
|
||||||
<h1>{{ settings.title }} (<a href="{{link}}">/{{ board }}/</a>)</h1>
|
<h1>{{ settings.title }} (<a href="{{link}}">/{{ board }}/</a>)</h1>
|
||||||
<div class="subtitle">{{ settings.subtitle }}</div>
|
<div class="subtitle">{{ settings.subtitle }}</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ul style="display: none">
|
|
||||||
<li id="sort-bump-order" class="sort" data-sort="data-bump" data-order="asc">{% trans 'Bump order' %}</li>
|
|
||||||
<li id="sort-creation-date" class="sort" data-sort="data-time" data-order="asc">{% trans 'Creation date' %}</li>
|
|
||||||
<li id="sort-reply-count" class="sort" data-sort="data-reply" data-order="asc">{% trans 'Reply count' %}</li>
|
|
||||||
<li id="sort-random" class="sort" data-sort="random">{% trans 'Random' %}</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<span>{% trans 'Sort by' %}: </span>
|
<span>{% trans 'Sort by' %}: </span>
|
||||||
<select id="sort_by" style="display: inline-block">
|
<select id="sort_by" style="display: inline-block">
|
||||||
<option selected value="bump-order">{% trans 'Bump order' %}</option>
|
<option selected value="bump:desc">{% trans 'Bump order' %}</option>
|
||||||
<option value="creation-date">{% trans 'Creation date' %}</option>
|
<option value="time:desc">{% trans 'Creation date' %}</option>
|
||||||
<option value="reply-count">{% trans 'Reply count' %}</option>
|
<option value="reply:desc">{% trans 'Reply count' %}</option>
|
||||||
<option value="random">{% trans 'Random' %}</option>
|
<option value="random:desc">{% trans 'Random' %}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<span>{% trans 'Image size' %}: </span>
|
<span>{% trans 'Image size' %}: </span>
|
||||||
|
@ -38,12 +32,15 @@
|
||||||
<option value="large">{% trans 'Large' %}</option>
|
<option value="large">{% trans 'Large' %}</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="threads">
|
<div class="threads">
|
||||||
<ul id="Grid">
|
<div id="Grid">
|
||||||
{% for post in recent_posts %}
|
{% for post in recent_posts %}
|
||||||
<li class="mix"
|
<div class="mix"
|
||||||
data-reply="{{ post.reply_count }}"
|
data-reply="{{ post.reply_count }}"
|
||||||
data-bump="{{ post.bump }}"
|
data-bump="{{ post.bump }}"
|
||||||
data-time="{{ post.time }}"
|
data-time="{{ post.time }}"
|
||||||
|
data-id="{{ post.id }}"
|
||||||
|
data-sticky="{% if post.sticky %}true{% else %}false{% endif %}"
|
||||||
|
data-locked="{% if post.locked %}true{% else %}false{% endif %}"
|
||||||
>
|
>
|
||||||
<div class="thread grid-li grid-size-small">
|
<div class="thread grid-li grid-size-small">
|
||||||
<a href="{{post.link}}">
|
<a href="{{post.link}}">
|
||||||
|
@ -55,7 +52,7 @@
|
||||||
id="img-{{ post.id }}" data-subject="{% if post.subject %}{{ post.subject|e }}{% endif %}" data-name="{{ post.name|e }}" data-muhdifference="{{ post.muhdifference }}" class="{{post.board}} thread-image" title="{{post.bump|date('%b %d %H:%M')}}">
|
id="img-{{ post.id }}" data-subject="{% if post.subject %}{{ post.subject|e }}{% endif %}" data-name="{{ post.name|e }}" data-muhdifference="{{ post.muhdifference }}" class="{{post.board}} thread-image" title="{{post.bump|date('%b %d %H:%M')}}">
|
||||||
</a>
|
</a>
|
||||||
<div class="replies">
|
<div class="replies">
|
||||||
<strong>R: {{ post.reply_count }} / I: {{ post.image_count }}</strong>
|
<strong>R: {{ post.reply_count }} / I: {{ post.image_count }}{% if post.sticky %} (sticky){% endif %}</strong>
|
||||||
{% if post.subject %}
|
{% if post.subject %}
|
||||||
<p class="intro">
|
<p class="intro">
|
||||||
<span class="subject">
|
<span class="subject">
|
||||||
|
@ -69,17 +66,17 @@
|
||||||
{{ post.body }}
|
{{ post.body }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<footer>
|
<footer>
|
||||||
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
|
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
|
||||||
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
|
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
|
||||||
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright © 2010-2014 Tinyboard Development Group
|
<br>Tinyboard Copyright © 2010-2014 Tinyboard Development Group
|
||||||
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2015 vichan-devel</p>
|
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2016 vichan-devel</p>
|
||||||
</footer>
|
</footer>
|
||||||
<script type="text/javascript">{% raw %}
|
<script type="text/javascript">{% raw %}
|
||||||
var styles = {
|
var styles = {
|
||||||
|
|
19
templates/themes/catalog/index.rss
Normal file
19
templates/themes/catalog/index.rss
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
|
||||||
|
<channel>
|
||||||
|
<title>/{{ board.uri }}/ - {{ board.title|e }}</title>
|
||||||
|
<link>{{ config.root }}{{ board.uri }}/</link>
|
||||||
|
<description>Live feed of new threads on the board /{{ board.uri }}/ - {{ board.title|e }}.</description>
|
||||||
|
<atom:link href="{{ config.root }}{{ board.uri }}/index.rss" rel="self" type="application/rss+xml"/>
|
||||||
|
{% for post in recent_posts %}
|
||||||
|
<item>
|
||||||
|
<title>{% if post.subject %}{{ post.subject|e }}{% else %}{{ post.body_nomarkup[:256]|remove_modifiers|e }}{% endif %}</title>
|
||||||
|
<link>{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html</link>
|
||||||
|
<guid>{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html</guid>
|
||||||
|
<comments>{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html</comments>
|
||||||
|
<pubDate>{{ post.pubdate }}</pubDate>
|
||||||
|
<description><![CDATA[ <a href='{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html' target=_blank><img style='float:left;margin:8px' border=0 src='{% if not config.uri_thumb %}{{ config.root }}{% endif %}{{ post.file }}'></a> {{ post.body }} ]]></description>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
|
@ -16,20 +16,25 @@
|
||||||
if ($action == 'all') {
|
if ($action == 'all') {
|
||||||
foreach ($boards as $board) {
|
foreach ($boards as $board) {
|
||||||
$b = new Catalog();
|
$b = new Catalog();
|
||||||
if ($config['smart_build']) {
|
|
||||||
|
$action = generation_strategy("sb_catalog", array($board));
|
||||||
|
if ($action == 'delete') {
|
||||||
file_unlink($config['dir']['home'] . $board . '/catalog.html');
|
file_unlink($config['dir']['home'] . $board . '/catalog.html');
|
||||||
|
file_unlink($config['dir']['home'] . $board . '/index.rss');
|
||||||
}
|
}
|
||||||
else {
|
elseif ($action == 'rebuild') {
|
||||||
$b->build($settings, $board);
|
$b->build($settings, $board);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && in_array($board, $boards)) {
|
} elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && in_array($board, $boards)) {
|
||||||
$b = new Catalog();
|
$b = new Catalog();
|
||||||
|
|
||||||
if ($config['smart_build']) {
|
$action = generation_strategy("sb_catalog", array($board));
|
||||||
|
if ($action == 'delete') {
|
||||||
file_unlink($config['dir']['home'] . $board . '/catalog.html');
|
file_unlink($config['dir']['home'] . $board . '/catalog.html');
|
||||||
|
file_unlink($config['dir']['home'] . $board . '/index.rss');
|
||||||
}
|
}
|
||||||
else {
|
elseif ($action == 'rebuild') {
|
||||||
$b->build($settings, $board);
|
$b->build($settings, $board);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,9 +93,12 @@
|
||||||
$post['file'] = $config['uri_thumb'] . $files[0]->thumb;
|
$post['file'] = $config['uri_thumb'] . $files[0]->thumb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$post['file'] = $config['root'] . $config['image_deleted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($post['image_count'])) $post['image_count'] = 0;
|
if (empty($post['image_count'])) $post['image_count'] = 0;
|
||||||
|
$post['pubdate'] = date('r', $post['time']);
|
||||||
$recent_posts[] = $post;
|
$recent_posts[] = $post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,5 +119,11 @@
|
||||||
'board' => $board_name,
|
'board' => $board_name,
|
||||||
'link' => $config['root'] . $board['dir']
|
'link' => $config['root'] . $board['dir']
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
file_write($config['dir']['home'] . $board_name . '/index.rss', Element('themes/catalog/index.rss', Array(
|
||||||
|
'config' => $config,
|
||||||
|
'recent_posts' => $recent_posts,
|
||||||
|
'board' => $board
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user