Conflicts:
	inc/config.php
	inc/functions.php
	templates/generic_page.html
	templates/index.html
This commit is contained in:
czaks 2013-08-03 19:05:25 -04:00
commit a0855cdcf5
22 changed files with 1304 additions and 1107 deletions

File diff suppressed because it is too large Load Diff

View File

@ -121,14 +121,14 @@ function query($query) {
return $pdo->query($query);
}
function db_error($PDOStatement=null) {
global $pdo;
function db_error($PDOStatement = null) {
global $pdo, $db_error;
if (isset($PDOStatement)) {
$err = $PDOStatement->errorInfo();
return $err[2];
$db_error = $PDOStatement->errorInfo();
return $db_error[2];
}
$err = $pdo->errorInfo();
return $err[2];
$db_error = $pdo->errorInfo();
return $db_error[2];
}

View File

@ -57,8 +57,8 @@ function createBoardlist($mod=false) {
);
}
function error($message, $priority = true) {
global $board, $mod, $config;
function error($message, $priority = true, $debug_stuff = false) {
global $board, $mod, $config, $db_error;
if ($config['syslog'] && $priority !== false) {
// Use LOG_NOTICE instead of LOG_ERR or LOG_WARNING because most error message are not significant.
@ -70,17 +70,21 @@ function error($message, $priority = true) {
die('Error: ' . $message . "\n");
}
if ($config['debug'] && isset($db_error)) {
$debug_stuff = array_combine(array('SQLSTATE', 'Error code', 'Error message'), $db_error);
}
die(Element('page.html', array(
'config'=>$config,
'title'=>_('Error'),
'subtitle'=>_('An error has occured.'),
'body'=>'<center>' .
'<h2>' . _($message) . '</h2>' .
(isset($board) ?
"<p><a href=\"" . $config['root'] .
($mod ? $config['file_mod'] . '?/' : '') .
$board['dir'] . $config['file_index'] . "\">"._("Go back")."</a>.</p>" : '') .
'</center>'
'config' => $config,
'title' => _('Error'),
'subtitle' => _('An error has occured.'),
'body' => Element('error.html', array(
'config' => $config,
'message' => $message,
'mod' => $mod,
'board' => isset($board) ? $board : false,
'debug' => is_array($debug_stuff) ? str_replace("\n", '&#10;', utf8tohtml(print_r($debug_stuff, true))) : utf8tohtml($debug_stuff)
))
)));
}
@ -319,31 +323,31 @@ class Post {
// Delete
if (hasPermission($config['mod']['delete'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_delete'], 'Delete', 'Are you sure you want to delete this?', $board['uri'] . '/delete/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_delete'], 'Delete', 'Are you sure you want to delete this?', $board['dir'] . 'delete/' . $this->id);
// Delete all posts by IP
if (hasPermission($config['mod']['deletebyip'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip'], 'Delete all posts by IP', 'Are you sure you want to delete all posts by this IP address?', $board['uri'] . '/deletebyip/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip'], 'Delete all posts by IP', 'Are you sure you want to delete all posts by this IP address?', $board['dir'] . 'deletebyip/' . $this->id);
// Delete all posts by IP (global)
if (hasPermission($config['mod']['deletebyip_global'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip_global'], 'Delete all posts by IP across all boards', 'Are you sure you want to delete all posts by this IP address, across all boards?', $board['uri'] . '/deletebyip/' . $this->id . '/global');
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip_global'], 'Delete all posts by IP across all boards', 'Are you sure you want to delete all posts by this IP address, across all boards?', $board['dir'] . 'deletebyip/' . $this->id . '/global');
// Ban
if (hasPermission($config['mod']['ban'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Ban').'" href="?/' . $board['uri'] . '/ban/' . $this->id . '">' . $config['mod']['link_ban'] . '</a>';
$built .= ' <a title="'._('Ban').'" href="?/' . $board['dir'] . 'ban/' . $this->id . '">' . $config['mod']['link_ban'] . '</a>';
// Ban & Delete
if (hasPermission($config['mod']['bandelete'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Ban & Delete').'" href="?/' . $board['uri'] . '/ban&amp;delete/' . $this->id . '">' . $config['mod']['link_bandelete'] . '</a>';
$built .= ' <a title="'._('Ban & Delete').'" href="?/' . $board['dir'] . 'ban&amp;delete/' . $this->id . '">' . $config['mod']['link_bandelete'] . '</a>';
// Delete file (keep post)
if (!empty($this->file) && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletefile'], _('Delete file'), _('Are you sure you want to delete this file?'), $board['uri'] . '/deletefile/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_deletefile'], _('Delete file'), _('Are you sure you want to delete this file?'), $board['dir'] . 'deletefile/' . $this->id);
// Edit post
if (hasPermission($config['mod']['editpost'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Edit post').'" href="?/' . $board['uri'] . '/edit' . ($config['mod']['raw_html_default'] ? '_raw' : '') . '/' . $this->id . '">' . $config['mod']['link_editpost'] . '</a>';
$built .= ' <a title="'._('Edit post').'" href="?/' . $board['dir'] . 'edit' . ($config['mod']['raw_html_default'] ? '_raw' : '') . '/' . $this->id . '">' . $config['mod']['link_editpost'] . '</a>';
if (!empty($built))
$built = '<span class="controls">' . $built . '</span>';
@ -420,54 +424,54 @@ class Thread {
// Mod controls (on posts)
// Delete
if (hasPermission($config['mod']['delete'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_delete'], _('Delete'), _('Are you sure you want to delete this?'), $board['uri'] . '/delete/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_delete'], _('Delete'), _('Are you sure you want to delete this?'), $board['dir'] . 'delete/' . $this->id);
// Delete all posts by IP
if (hasPermission($config['mod']['deletebyip'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip'], _('Delete all posts by IP'), _('Are you sure you want to delete all posts by this IP address?'), $board['uri'] . '/deletebyip/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip'], _('Delete all posts by IP'), _('Are you sure you want to delete all posts by this IP address?'), $board['dir'] . 'deletebyip/' . $this->id);
// Delete all posts by IP (global)
if (hasPermission($config['mod']['deletebyip_global'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip_global'], _('Delete all posts by IP across all boards'), _('Are you sure you want to delete all posts by this IP address, across all boards?'), $board['uri'] . '/deletebyip/' . $this->id . '/global');
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip_global'], _('Delete all posts by IP across all boards'), _('Are you sure you want to delete all posts by this IP address, across all boards?'), $board['dir'] . 'deletebyip/' . $this->id . '/global');
// Ban
if (hasPermission($config['mod']['ban'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Ban').'" href="?/' . $board['uri'] . '/ban/' . $this->id . '">' . $config['mod']['link_ban'] . '</a>';
$built .= ' <a title="'._('Ban').'" href="?/' . $board['dir'] . 'ban/' . $this->id . '">' . $config['mod']['link_ban'] . '</a>';
// Ban & Delete
if (hasPermission($config['mod']['bandelete'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Ban & Delete').'" href="?/' . $board['uri'] . '/ban&amp;delete/' . $this->id . '">' . $config['mod']['link_bandelete'] . '</a>';
$built .= ' <a title="'._('Ban & Delete').'" href="?/' . $board['dir'] . 'ban&amp;delete/' . $this->id . '">' . $config['mod']['link_bandelete'] . '</a>';
// Delete file (keep post)
if (!empty($this->file) && $this->file != 'deleted' && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod))
$built .= ' ' . secure_link_confirm($config['mod']['link_deletefile'], _('Delete file'), _('Are you sure you want to delete this file?'), $board['uri'] . '/deletefile/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_deletefile'], _('Delete file'), _('Are you sure you want to delete this file?'), $board['dir'] . 'deletefile/' . $this->id);
// Sticky
if (hasPermission($config['mod']['sticky'], $board['uri'], $this->mod))
if ($this->sticky)
$built .= ' <a title="'._('Make thread not sticky').'" href="?/' . secure_link($board['uri'] . '/unsticky/' . $this->id) . '">' . $config['mod']['link_desticky'] . '</a>';
$built .= ' <a title="'._('Make thread not sticky').'" href="?/' . secure_link($board['dir'] . 'unsticky/' . $this->id) . '">' . $config['mod']['link_desticky'] . '</a>';
else
$built .= ' <a title="'._('Make thread sticky').'" href="?/' . secure_link($board['uri'] . '/sticky/' . $this->id) . '">' . $config['mod']['link_sticky'] . '</a>';
$built .= ' <a title="'._('Make thread sticky').'" href="?/' . secure_link($board['dir'] . 'sticky/' . $this->id) . '">' . $config['mod']['link_sticky'] . '</a>';
if (hasPermission($config['mod']['bumplock'], $board['uri'], $this->mod))
if ($this->bumplocked)
$built .= ' <a title="'._('Allow thread to be bumped').'" href="?/' . secure_link($board['uri'] . '/bumpunlock/' . $this->id) . '">' . $config['mod']['link_bumpunlock'] . '</a>';
$built .= ' <a title="'._('Allow thread to be bumped').'" href="?/' . secure_link($board['dir'] . 'bumpunlock/' . $this->id) . '">' . $config['mod']['link_bumpunlock'] . '</a>';
else
$built .= ' <a title="'._('Prevent thread from being bumped').'" href="?/' . secure_link($board['uri'] . '/bumplock/' . $this->id) . '">' . $config['mod']['link_bumplock'] . '</a>';
$built .= ' <a title="'._('Prevent thread from being bumped').'" href="?/' . secure_link($board['dir'] . 'bumplock/' . $this->id) . '">' . $config['mod']['link_bumplock'] . '</a>';
// Lock
if (hasPermission($config['mod']['lock'], $board['uri'], $this->mod))
if ($this->locked)
$built .= ' <a title="'._('Unlock thread').'" href="?/' . secure_link($board['uri'] . '/unlock/' . $this->id) . '">' . $config['mod']['link_unlock'] . '</a>';
$built .= ' <a title="'._('Unlock thread').'" href="?/' . secure_link($board['dir'] . 'unlock/' . $this->id) . '">' . $config['mod']['link_unlock'] . '</a>';
else
$built .= ' <a title="'._('Lock thread').'" href="?/' . secure_link($board['uri'] . '/lock/' . $this->id) . '">' . $config['mod']['link_lock'] . '</a>';
$built .= ' <a title="'._('Lock thread').'" href="?/' . secure_link($board['dir'] . 'lock/' . $this->id) . '">' . $config['mod']['link_lock'] . '</a>';
if (hasPermission($config['mod']['move'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Move thread to another board').'" href="?/' . $board['uri'] . '/move/' . $this->id . '">' . $config['mod']['link_move'] . '</a>';
$built .= ' <a title="'._('Move thread to another board').'" href="?/' . $board['dir'] . 'move/' . $this->id . '">' . $config['mod']['link_move'] . '</a>';
// Edit post
if (hasPermission($config['mod']['editpost'], $board['uri'], $this->mod))
$built .= ' <a title="'._('Edit post').'" href="?/' . $board['uri'] . '/edit' . ($config['mod']['raw_html_default'] ? '_raw' : '') . '/' . $this->id . '">' . $config['mod']['link_editpost'] . '</a>';
$built .= ' <a title="'._('Edit post').'" href="?/' . $board['dir'] . 'edit' . ($config['mod']['raw_html_default'] ? '_raw' : '') . '/' . $this->id . '">' . $config['mod']['link_editpost'] . '</a>';
if (!empty($built))
$built = '<span class="controls op">' . $built . '</span>';

View File

@ -132,10 +132,10 @@ class Filter {
function do_filters(array $post) {
global $config;
if (!isset($config['flood_filters']))
if (!isset($config['filters']))
return;
foreach ($config['flood_filters'] as $arr) {
foreach ($config['filters'] as $arr) {
$filter = new Filter($arr);
if ($filter->check($post))
$filter->action();

File diff suppressed because it is too large Load Diff

View File

@ -279,26 +279,24 @@ class ImageConvert extends ImageBase {
}
$this->temp = tempnam($config['tmp'], 'imagick');
$quality = $config['thumb_quality'] * 10;
$config['thumb_keep_animation_frames'] = (int) $config['thumb_keep_animation_frames'];
$config['thumb_keep_animation_frames'] = (int)$config['thumb_keep_animation_frames'];
if ($this->format == 'gif' && ($config['thumb_ext'] == 'gif' || $config['thumb_ext'] == '') && $config['thumb_keep_animation_frames'] > 1) {
if ($this->gifsicle) {
if (shell_exec("gifsicle --unoptimize -O2 --resize {$this->width}x{$this->height} < " .
if (trim($error = shell_exec("gifsicle --unoptimize -O2 --resize {$this->width}x{$this->height} < " .
escapeshellarg($this->src . '') . " \"#0-{$config['thumb_keep_animation_frames']}\" > " .
escapeshellarg($this->temp)) || !file_exists($this->temp))
error('Failed to resize image!');
escapeshellarg($this->temp) . '2>&1 &&echo $?') !== '0') || !file_exists($this->temp))
error('Failed to resize image!', null, $error);
} else {
if (shell_exec("convert -background transparent -filter Point -sample {$this->width}x{$this->height} +antialias -quality {$quality} " .
escapeshellarg($this->src . '') . " " . escapeshellarg($this->temp)) || !file_exists($this->temp))
error('Failed to resize image!');
if (trim($error = shell_exec('convert ' . sprintf($config['convert_args'], '', $this->width, $this->height) . ' ' .
escapeshellarg($this->src) . ' ' . escapeshellarg($this->temp) . ' 2>&1 &&echo $?')) !== '0' || !file_exists($this->temp))
error('Failed to resize image!', null, $error);
}
} else {
if (shell_exec("convert -background transparent -flatten -filter Point -scale {$this->width}x{$this->height} +antialias -quality {$quality} " .
escapeshellarg($this->src . '[0]') . " " . escapeshellarg($this->temp)) || !file_exists($this->temp))
error('Failed to resize image!');
if (trim($error = shell_exec('convert ' . sprintf($config['convert_args'], '-flatten', $this->width, $this->height) . ' ' .
escapeshellarg($this->src . '[0]') . " " . escapeshellarg($this->temp) . ' 2>&1 &&echo $?')) !== '0' || !file_exists($this->temp))
error('Failed to resize image!', null, $error);
}
}
}
@ -309,7 +307,7 @@ class ImagePNG extends ImageBase {
}
public function to($src) {
global $config;
imagepng($this->image, $src, $config['thumb_quality']);
imagepng($this->image, $src);
}
public function resize() {
$this->GD_create();
@ -322,7 +320,7 @@ class ImagePNG extends ImageBase {
class ImageGIF extends ImageBase {
public function from() {
$this->image = @imagecreatefromgif ($this->src);
$this->image = @imagecreatefromgif($this->src);
}
public function to($src) {
imagegif ($this->image, $src);

View File

@ -13,13 +13,14 @@ function config_vars() {
'default_temp' => false
);
$temp_comment = false;
$line_no = 0;
foreach ($config_file as $line) {
if ($temp_comment) {
$var['comment'][] = $temp_comment;
$temp_comment = false;
}
if (preg_match('!^\s*// (.*)$!', $line, $matches)) {
if (preg_match('!^\s*// ([^$].*)$!', $line, $matches)) {
if ($var['default'] !== false) {
$line = '';
$temp_comment = $matches[1];
@ -28,7 +29,10 @@ function config_vars() {
}
} else if ($var['default_temp'] !== false) {
$var['default_temp'] .= "\n" . $line;
} elseif (preg_match('!^\s*\$config\[(.+?)\] = (.+?)(;( //.+)?)?$!', $line, $matches)) {
} elseif (preg_match('!^[\s/]*\$config\[(.+?)\] = (.+?)(;( //.+)?)?$!', $line, $matches)) {
if (preg_match('!^\s*//\s*!', $line)) {
$var['commented'] = true;
}
$var['name'] = explode('][', $matches[1]);
if (count($var['name']) == 1) {
$var['name'] = preg_replace('/^\'(.*)\'$/', '$1', end($var['name']));
@ -43,20 +47,37 @@ function config_vars() {
$var['default_temp'] = $matches[2];
}
if (trim($line) === '') {
if ($var['name'] !== false) {
if ($var['default_temp'])
$var['default'] = $var['default_temp'];
$temp = eval('return ' . $var['default'] . ';');
if (!isset($temp))
if ($var['name'] !== false) {
if ($var['default_temp'])
$var['default'] = $var['default_temp'];
if ($var['default'][0] == '&')
continue; // This is just an alias.
if (!preg_match('/^array|\[\]|function/', $var['default']) && !preg_match('/^Example: /', trim(implode(' ', $var['comment'])))) {
$syntax_error = true;
$temp = eval('$syntax_error = false;return ' . $var['default'] . ';');
if ($syntax_error && $temp === false) {
error('Error parsing config.php (line ' . $line_no . ')!', null, $var);
} elseif (!isset($temp)) {
$var['type'] = 'unknown';
else
} else {
$var['type'] = gettype($temp);
}
if ($var['type'] == 'integer' && $var['name'][0] == 'mod' &&
(in_array($var['default'], array('JANITOR', 'MOD', 'ADMIN', 'DISABLED')) || mb_strpos($var['default'], "\$config['mod']") === 0)) {
// Permissions variable
$var['permissions'] = true;
}
unset($var['default_temp']);
if (!is_array($var['name']) || (end($var['name']) != '' && !in_array(reset($var['name']), array('stylesheets')))) {
$already_exists = false;
foreach ($conf as $_var) {
if ($var['name'] == $_var['name'])
$already_exists = true;
}
if (!$already_exists)
$conf[] = $var;
}
}
@ -65,9 +86,17 @@ function config_vars() {
'name' => false,
'comment' => array(),
'default' => false,
'default_temp' => false
'default_temp' => false,
'commented' => false,
'permissions' => false,
);
}
if (trim($line) === '') {
$var['comment'] = array();
}
$line_no++;
}
return $conf;

View File

@ -800,21 +800,21 @@ function mod_page_ip($ip) {
$args['token'] = make_secure_link_token('ban');
if (hasPermission($config['mod']['view_ban'])) {
$query = prepare("SELECT ``bans``.*, `username` FROM ``bans`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `ip` = :ip");
$query = prepare("SELECT ``bans``.*, `username` FROM ``bans`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `ip` = :ip ORDER BY `set` DESC");
$query->bindValue(':ip', $ip);
$query->execute() or error(db_error($query));
$args['bans'] = $query->fetchAll(PDO::FETCH_ASSOC);
}
if (hasPermission($config['mod']['view_notes'])) {
$query = prepare("SELECT ``ip_notes``.*, `username` FROM ``ip_notes`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `ip` = :ip");
$query = prepare("SELECT ``ip_notes``.*, `username` FROM ``ip_notes`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `ip` = :ip ORDER BY `time` DESC");
$query->bindValue(':ip', $ip);
$query->execute() or error(db_error($query));
$args['notes'] = $query->fetchAll(PDO::FETCH_ASSOC);
}
if (hasPermission($config['mod']['modlog_ip'])) {
$query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `text` LIKE :search ORDER BY `time` DESC LIMIT 20");
$query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `text` LIKE :search ORDER BY `time` DESC LIMIT 50");
$query->bindValue(':search', '%' . $ip . '%');
$query->execute() or error(db_error($query));
$args['logs'] = $query->fetchAll(PDO::FETCH_ASSOC);
@ -1941,30 +1941,43 @@ function mod_report_dismiss($id, $all = false) {
}
function mod_config() {
global $config, $mod;
function mod_config($board_config = false) {
global $config, $mod, $board;
if (!hasPermission($config['mod']['edit_config']))
if ($board_config && !openBoard($board_config))
error($config['error']['noboard']);
if (!hasPermission($config['mod']['edit_config'], $board_config))
error($config['error']['noaccess']);
$config_file = $board_config ? $board['dir'] . 'config.php' : 'inc/instance-config.php';
if ($config['mod']['config_editor_php']) {
$readonly = !is_writable('inc/instance-config.php');
$readonly = !(is_file($config_file) ? is_writable($config_file) : is_writable(dirname($config_file)));
if (!$readonly && isset($_POST['code'])) {
$code = $_POST['code'];
file_put_contents('inc/instance-config.php', $code);
header('Location: ?/config', true, $config['redirect_http']);
file_put_contents($config_file, $code);
header('Location: ?/config' . ($board_config ? '/' . $board_config : ''), true, $config['redirect_http']);
return;
}
$instance_config = file_get_contents('inc/instance-config.php');
$instance_config = @file_get_contents($config_file);
if ($instance_config === false) {
$instance_config = "<?php\n\n// This file does not exist yet. You are creating it.";
}
$instance_config = str_replace("\n", '&#010;', utf8tohtml($instance_config));
mod_page(_('Config editor'), 'mod/config-editor-php.html', array('php' => $instance_config, 'readonly' => $readonly));
mod_page(_('Config editor'), 'mod/config-editor-php.html', array(
'php' => $instance_config,
'readonly' => $readonly,
'boards' => listBoards(),
'board' => $board_config,
'file' => $config_file
));
return;
}
require_once 'inc/mod/config-editor.php';
$conf = config_vars();
@ -1975,7 +1988,7 @@ function mod_config() {
foreach ($var['name'] as $n)
$c = &$c[$n];
} else {
$c = $config[$var['name']];
$c = @$config[$var['name']];
}
$var['value'] = $c;
@ -2010,14 +2023,28 @@ function mod_config() {
$config_append .= '[' . var_export($var['name'], true) . ']';
}
$config_append .= ' = ' . var_export($value, true) . ";\n";
$config_append .= ' = ';
if (@$var['permissions'] && in_array($value, array(JANITOR, MOD, ADMIN, DISABLED))) {
$perm_array = array(
JANITOR => 'JANITOR',
MOD => 'MOD',
ADMIN => 'ADMIN',
DISABLED => 'DISABLED'
);
$config_append .= $perm_array[$value];
} else {
$config_append .= var_export($value, true);
}
$config_append .= ";\n";
}
}
if (!empty($config_append)) {
$config_append = "\n// Changes made via web editor by \"" . $mod['username'] . "\" @ " . date('r') . ":\n" . $config_append . "\n";
if (!@file_put_contents('inc/instance-config.php', $config_append, FILE_APPEND)) {
if (!is_file($config_file))
$config_append = "<?php\n\n$config_append";
if (!@file_put_contents($config_file, $config_append, FILE_APPEND)) {
$config_append = htmlentities($config_append);
if ($config['minify_html'])
@ -2026,8 +2053,8 @@ function mod_config() {
$page['title'] = 'Cannot write to file!';
$page['config'] = $config;
$page['body'] = '
<p style="text-align:center">Tinyboard could not write to <strong>inc/instance-config.php</strong> with the ammended configuration, probably due to a permissions error.</p>
<p style="text-align:center">You may proceed with these changes manually by copying and pasting the following code to the end of <strong>inc/instance-config.php</strong>:</p>
<p style="text-align:center">Tinyboard could not write to <strong>' . $config_file . '</strong> with the ammended configuration, probably due to a permissions error.</p>
<p style="text-align:center">You may proceed with these changes manually by copying and pasting the following code to the end of <strong>' . $config_file . '</strong>:</p>
<textarea style="width:700px;height:370px;margin:auto;display:block;background:white;color:black" readonly>' . $config_append . '</textarea>
';
echo Element('page.html', $page);
@ -2035,12 +2062,18 @@ function mod_config() {
}
}
header('Location: ?/', true, $config['redirect_http']);
header('Location: ?/config', true, $config['redirect_http']);
exit;
}
mod_page(_('Config editor'), 'mod/config-editor.html', array('conf' => $conf));
mod_page(_('Config editor') . ($board_config ? ': ' . sprintf($config['board_abbreviation'], $board_config) : ''),
'mod/config-editor.html', array(
'boards' => listBoards(),
'board' => $board_config,
'conf' => $conf,
'file' => $config_file
));
}
function mod_themes_list() {

View File

@ -459,7 +459,7 @@ if ($step == 0) {
array(
'category' => 'Database',
'name' => 'MySQL PDO driver installed',
'result' => extension_loaded('pdo') && in_array('mysql1', PDO::getAvailableDrivers()),
'result' => extension_loaded('pdo') && in_array('mysql', PDO::getAvailableDrivers()),
'required' => true,
'message' => 'The required <a href="http://www.php.net/manual/en/ref.pdo-mysql.php">PDO MySQL driver</a> is not installed.',
),
@ -494,7 +494,7 @@ if ($step == 0) {
array(
'category' => 'Image processing',
'name' => 'Imagick extension installed',
'result' => extension_loaded('imagick1'),
'result' => extension_loaded('imagick'),
'required' => false,
'message' => '(Optional) The PHP <a href="http://www.php.net/manual/en/imagick.installation.php">Imagick</a> (ImageMagick) extension is not installed. You may not use Imagick for better (and faster) image processing.',
),
@ -515,7 +515,7 @@ if ($step == 0) {
array(
'category' => 'Image processing',
'name' => '`gifsicle` (command-line animted GIF thumbnailing)',
'result' => $can_exec && shell_exec('which gifsicle1'),
'result' => $can_exec && shell_exec('which gifsicle'),
'required' => false,
'message' => '(Optional) `gifsicle` was not found or executable; you may not use `convert+gifsicle` for better animated GIF thumbnailing.',
),
@ -532,6 +532,13 @@ if ($step == 0) {
'result' => is_writable('inc/instance-config.php'),
'required' => false,
'message' => 'Tinyboard does not have permission to make changes to inc/instance-config.php. To complete the installation, you will be asked to manually copy and paste code into the file instead.'
),
array(
'category' => 'Misc',
'name' => 'Tinyboard installed using git',
'result' => is_dir('.git.'),
'required' => false,
'message' => 'Tinyboard is still beta software and it\'s not going to come out of beta any time soon. As there are often many months between releases yet changes and bug fixes are very frequent, it\'s recommended to use the git repository to maintain your Tinyboard installation. Using git makes upgrading much easier.'
)
);
@ -550,167 +557,16 @@ if ($step == 0) {
// Basic config
$page['title'] = 'Configuration';
function create_salt() {
return substr(base64_encode(sha1(rand())), 0, rand(25, 31));
}
$config['cookies']['salt'] = substr(base64_encode(sha1(rand())), 0, 30);
$config['secure_trip_salt'] = substr(base64_encode(sha1(rand())), 0, 30);
$page['body'] = '
<form action="?step=3" method="post">
<fieldset>
<legend>Database</legend>
<label for="db_type">Type:</label>
<select id="db_type" name="db[type]">';
$drivers = PDO::getAvailableDrivers();
foreach ($drivers as &$driver) {
$driver_txt = $driver;
switch ($driver) {
case 'cubrid':
$driver_txt = 'Cubrid';
break;
case 'dblib':
$driver_txt = 'FreeTDS / Microsoft SQL Server / Sybase';
break;
case 'firebird':
$driver_txt = 'Firebird/Interbase 6';
break;
case 'ibm':
$driver_txt = 'IBM DB2';
break;
case 'informix':
$driver_txt = 'IBM Informix Dynamic Server';
break;
case 'mysql':
$driver_txt = 'MySQL';
break;
case 'oci':
$driver_txt = 'OCI';
break;
case 'odbc':
$driver_txt = 'ODBC v3 (IBM DB2, unixODBC)';
break;
case 'pgsql':
$driver_txt = 'PostgreSQL';
break;
case 'sqlite':
$driver_txt = 'SQLite 3';
break;
case 'sqlite2':
$driver_txt = 'SQLite 2';
break;
}
$page['body'] .= '<option value="' . $driver . '">' . $driver_txt . '</option>';
}
$page['body'] .= '
</select>
<label for="db_server">Server:</label>
<input type="text" id="db_server" name="db[server]" value="localhost" />
<label for="db_db">Database:</label>
<input type="text" id="db_db" name="db[database]" value="" />
<label for="db_prefix">Table prefix (optional):</label>
<input type="text" id="db_prefix" name="db[prefix]" value="" />
<label for="db_user">Username:</label>
<input type="text" id="db_user" name="db[user]" value="" />
<label for="db_pass">Password:</label>
<input type="password" id="db_pass" name="db[password]" value="" />
</fieldset>
<p style="text-align:center" class="unimportant">The following is all later configurable. For more options, <a href="http://tinyboard.org/docs/?p=Config">edit your configuration files</a> after installing.</p>
<fieldset>
<legend>Cookies</legend>
<label for="cookies_mod">Moderator cookie:</label>
<input type="text" id="cookies_mod" name="cookies[mod]" value="' . $config['cookies']['mod'] . '" />
<label for="cookies_salt">Secure salt:</label>
<input type="text" id="cookies_salt" name="cookies[salt]" value="' . create_salt() . '" size="40" />
</fieldset>
<fieldset>
<legend>Flood control</legend>
<label for="flood_time">Seconds before each post:</label>
<input type="text" id="flood_time" name="flood_time" value="' . $config['flood_time'] . '" />
<label for="flood_time_ip">Seconds before you can repost something (post the exact same text):</label>
<input type="text" id="flood_time_ip" name="flood_time_ip" value="' . $config['flood_time_ip'] . '" />
<label for="flood_time_same">Same as above, but with a different IP address:</label>
<input type="text" id="flood_time_same" name="flood_time_same" value="' . $config['flood_time_same'] . '" />
<label for="max_body">Maximum post body length:</label>
<input type="text" id="max_body" name="max_body" value="' . $config['max_body'] . '" />
<label for="reply_limit">Replies in a thread before it can no longer be bumped:</label>
<input type="text" id="reply_limit" name="reply_limit" value="' . $config['reply_limit'] . '" />
<label for="max_links">Maximum number of links in a single post:</label>
<input type="text" id="max_links" name="max_links" value="' . $config['max_links'] . '" />
</fieldset>
<fieldset>
<legend>Images</legend>
<label for="max_filesize">Maximum image filesize:</label>
<input type="text" id="max_filesize" name="max_filesize" value="' . $config['max_filesize'] . '" />
<label for="thumb_width">Thumbnail width:</label>
<input type="text" id="thumb_width" name="thumb_width" value="' . $config['thumb_width'] . '" />
<label for="thumb_height">Thumbnail height:</label>
<input type="text" id="thumb_height" name="thumb_height" value="' . $config['thumb_height'] . '" />
<label for="max_width">Maximum image width:</label>
<input type="text" id="max_width" name="max_width" value="' . $config['max_width'] . '" />
<label for="max_height">Maximum image height:</label>
<input type="text" id="max_height" name="max_height" value="' . $config['max_height'] . '" />
</fieldset>
<fieldset>
<legend>Display</legend>
<label for="threads_per_page">Threads per page:</label>
<input type="text" id="threads_per_page" name="threads_per_page" value="' . $config['threads_per_page'] . '" />
<label for="max_pages">Page limit:</label>
<input type="text" id="max_pages" name="max_pages" value="' . $config['max_pages'] . '" />
<label for="threads_preview">Number of replies to show per thread on the index page:</label>
<input type="text" id="threads_preview" name="threads_preview" value="' . $config['threads_preview'] . '" />
</fieldset>
<fieldset>
<legend>Directories</legend>
<label for="root">Root URI (include trailing slash):</label>
<input type="text" id="root" name="root" value="' . $config['root'] . '" />
<label for="dir_img">Image directory:</label>
<input type="text" id="dir_img" name="dir[img]" value="' . $config['dir']['img'] . '" />
<label for="dir_thumb">Thumbnail directory:</label>
<input type="text" id="dir_thumb" name="dir[thumb]" value="' . $config['dir']['thumb'] . '" />
<label for="dir_res">Thread directory:</label>
<input type="text" id="dir_res" name="dir[res]" value="' . $config['dir']['res'] . '" />
</fieldset>
<fieldset>
<legend>Miscellaneous</legend>
<label for="secure_trip_salt">Secure trip (##) salt:</label>
<input type="text" id="secure_trip_salt" name="secure_trip_salt" value="' . create_salt() . '" size="40" />
</fieldset>
<p style="text-align:center">
<input type="submit" value="Complete installation" />
</p>
</form>
';
echo Element('page.html', $page);
echo Element('page.html', array(
'body' => Element('installer/config.html', array(
'config' => $config
)),
'title' => 'Configuration',
'config' => $config
));
} elseif ($step == 3) {
$instance_config =
'<?php
@ -771,7 +627,7 @@ if ($step == 0) {
sql_open();
if (mysql_version() < 50503)
$sql = str_replace('utf8', 'utf8mb4', $sql);
$sql = preg_replace('/(CHARSET=|CHARACTER SET )utf8mb4/', '$1utf8', $sql);
// This code is probably horrible, but what I'm trying
// to do is find all of the SQL queires and put them

84
mod.php
View File

@ -21,64 +21,65 @@ if (get_magic_quotes_gpc()) {
$query = isset($_SERVER['QUERY_STRING']) ? urldecode($_SERVER['QUERY_STRING']) : '';
$pages = array(
'' => ':?/', // redirect to dashboard
'/' => 'dashboard', // dashboard
'' => ':?/', // redirect to dashboard
'/' => 'dashboard', // dashboard
'/confirm/(.+)' => 'confirm', // confirm action (if javascript didn't work)
'/logout' => 'logout', // logout
'/logout' => 'logout', // logout
'/users' => 'users', // manage users
'/users/(\d+)' => 'user', // edit user
'/users/(\d+)/(promote|demote)' => 'user_promote', // prmote/demote user
'/users' => 'users', // manage users
'/users/(\d+)' => 'user', // edit user
'/users/(\d+)/(promote|demote)' => 'user_promote', // prmote/demote user
'/users/new' => 'user_new', // create a new user
'/new_PM/([^/]+)' => 'new_pm', // create a new pm
'/PM/(\d+)(/reply)?' => 'pm', // read a pm
'/inbox' => 'inbox', // pm inbox
'/PM/(\d+)(/reply)?' => 'pm', // read a pm
'/inbox' => 'inbox', // pm inbox
'/noticeboard' => 'noticeboard', // view noticeboard
'/noticeboard/(\d+)' => 'noticeboard', // view noticeboard
'/noticeboard/delete/(\d+)' => 'noticeboard_delete',// delete from noticeboard
'/log' => 'log', // modlog
'/log/(\d+)' => 'log', // modlog
'/noticeboard/(\d+)' => 'noticeboard', // view noticeboard
'/noticeboard/delete/(\d+)' => 'noticeboard_delete', // delete from noticeboard
'/log' => 'log', // modlog
'/log/(\d+)' => 'log', // modlog
'/log:([^/]+)' => 'user_log', // modlog
'/log:([^/]+)/(\d+)' => 'user_log', // modlog
'/news' => 'news', // view news
'/news/(\d+)' => 'news', // view news
'/news/delete/(\d+)' => 'news_delete', // delete from news
'/log:([^/]+)/(\d+)' => 'user_log', // modlog
'/news' => 'news', // view news
'/news/(\d+)' => 'news', // view news
'/news/delete/(\d+)' => 'news_delete', // delete from news
'/edit/(\%b)' => 'edit_board', // edit board details
'/new-board' => 'new_board', // create a new board
'/rebuild' => 'rebuild', // rebuild static files
'/reports' => 'reports', // report queue
'/reports/(\d+)/dismiss(all)?' => 'report_dismiss', // dismiss a report
'/rebuild' => 'rebuild', // rebuild static files
'/reports' => 'reports', // report queue
'/reports/(\d+)/dismiss(all)?' => 'report_dismiss', // dismiss a report
'/IP/([\w.:]+)' => 'ip', // view ip address
'/IP/([\w.:]+)' => 'ip', // view ip address
'/IP/([\w.:]+)/remove_note/(\d+)' => 'ip_remove_note', // remove note from ip address
'/bans' => 'bans', // ban list
'/bans/(\d+)' => 'bans', // ban list
'/bans' => 'bans', // ban list
'/bans/(\d+)' => 'bans', // ban list
'/search' => 'search_redirect', // search
'/search' => 'search_redirect', // search
'/search/(posts|IP_notes|bans|log)/(.+)/(\d+)' => 'search', // search
'/search/(posts|IP_notes|bans|log)/(.+)' => 'search', // search
'/search/(posts|IP_notes|bans|log)/(.+)' => 'search', // search
// CSRF-protected moderator actions
'/ban' => 'secure_POST ban', // new ban
'/(\%b)/ban(&delete)?/(\d+)' => 'secure_POST ban_post', // ban poster
'/(\%b)/move/(\d+)' => 'secure_POST move', // move thread
'/ban' => 'secure_POST ban', // new ban
'/(\%b)/ban(&delete)?/(\d+)' => 'secure_POST ban_post', // ban poster
'/(\%b)/move/(\d+)' => 'secure_POST move', // move thread
'/(\%b)/edit(_raw)?/(\d+)' => 'secure_POST edit_post', // edit post
'/(\%b)/delete/(\d+)' => 'secure delete', // delete post
'/(\%b)/deletefile/(\d+)' => 'secure deletefile', // delete file from post
'/(\%b)/delete/(\d+)' => 'secure delete', // delete post
'/(\%b)/deletefile/(\d+)' => 'secure deletefile', // delete file from post
'/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread
'/themes' => 'themes_list', // manage themes
'/themes' => 'themes_list', // manage themes
'/themes/(\w+)' => 'theme_configure', // configure/reconfigure theme
'/themes/(\w+)/rebuild' => 'theme_rebuild', // rebuild theme
'/themes/(\w+)/uninstall' => 'theme_uninstall', // uninstall theme
'/themes/(\w+)/rebuild' => 'theme_rebuild', // rebuild theme
'/themes/(\w+)/uninstall' => 'theme_uninstall', // uninstall theme
'/config' => 'config', // config editor
'/config' => 'config', // config editor
'/config/(\%b)' => 'config', // config editor
// these pages aren't listed in the dashboard without $config['debug']
'/debug/antispam' => 'debug_antispam',
@ -109,7 +110,7 @@ $new_pages = array();
foreach ($pages as $key => $callback) {
if (is_string($callback) && preg_match('/^secure /', $callback))
$key .= '(/(?P<token>[a-f0-9]{8}))?';
$key = str_replace('\%b', $config['board_regex'], $key);
$key = str_replace('\%b', '?P<board>' . sprintf(substr($config['board_path'], 0, -1), $config['board_regex']), $key);
$new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!u'] = $callback;
}
$pages = $new_pages;
@ -118,6 +119,15 @@ foreach ($pages as $uri => $handler) {
if (preg_match($uri, $query, $matches)) {
$matches = array_slice($matches, 1);
if (isset($matches['board'])) {
$board_match = $matches['board'];
unset($matches['board']);
$key = array_search($board_match, $matches);
if (preg_match('/^' . sprintf(substr($config['board_path'], 0, -1), '(' . $config['board_regex'] . ')') . '$/u', $matches[$key], $board_match)) {
$matches[$key] = $board_match[1];
}
}
if (is_string($handler) && preg_match('/^secure(_POST)? /', $handler, $m)) {
$secure_post_only = isset($m[1]);
if (!$secure_post_only || $_SERVER['REQUEST_METHOD'] == 'POST') {

View File

@ -218,7 +218,7 @@ if (isset($_POST['delete'])) {
}
if (!$post['mod']) {
$post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) && !($config['quick_reply'] && isset($_POST['quick-reply'])) ? $post['thread'] : null));
$post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) && !($config['quick_reply'] && isset($_POST['quick-reply'])) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null)));
if ($post['antispam_hash'] === true)
error($config['error']['spam']);
}
@ -430,7 +430,7 @@ if (isset($_POST['delete'])) {
$post['filehash'] = $config['file_hash']($upload);
$post['filesize'] = filesize($upload);
if ($is_an_image) {
if ($is_an_image && $config['ie_mime_type_detection'] !== false) {
// Check IE MIME type detection XSS exploit
$buffer = file_get_contents($upload, null, null, null, 255);
if (preg_match($config['ie_mime_type_detection'], $buffer)) {
@ -596,12 +596,15 @@ if (isset($_POST['delete'])) {
}
}
buildThread($post['op'] ? $id : $post['thread']);
if (!$post['op'] && strtolower($post['email']) != 'sage' && !$thread['sage'] && ($config['reply_limit'] == 0 || $numposts['replies']+1 < $config['reply_limit'])) {
bumpThread($post['thread']);
}
buildThread($post['op'] ? $id : $post['thread']);
if ($config['try_smarter'] && $post['op'])
$build_pages = range(1, $config['max_pages']);
if ($post['op'])
clean();

View File

@ -19,9 +19,12 @@ a.post_no {
color: #ccc;
}
div.post.reply, input, textarea {
background: rgba(0, 0, 0, 0.1)!important;
border: 1px solid rgba(0, 0, 0, 0.2)!important;
border-radius: 2px !important;
background: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 2px;
}
div.post.reply.post-hover {
background: rgba(200, 200, 200, 0.85);
}
div.post.reply.highlighted {
background: #f0c0b0;

View File

@ -95,7 +95,7 @@ form table tr th {
background: #7E3737 !important;
color: #B99B9B !important;
}
input[type="text"], input[type="password"], textarea {
input[type="text"], input[type="password"], textarea, select {
border: 2px solid rgba(94, 50, 50, 0.56) !important;
background: #7C7373 !important;
color: #311313 !important;

18
templates/error.html Normal file
View File

@ -0,0 +1,18 @@
<div style="text-align:center">
<h2 style="margin:20px 0">{{ message }}</h2>
{% if board %}
<p>
<a href="{{ config.root }}{% if mod %}{{ config.file_mod }}?/{% endif %}{{ board.dir }}{{ config.file_index }}">
{% trans 'Go back' %}
</a>
</p>
{% endif %}
</div>
{% if debug and config.debug %}
<hr>
<h3>{% trans 'Error information' %}</h3>
<pre style="white-space:pre-wrap;font-size: 10px;">
{{ debug }}
</pre>
<hr>
{% endif %}

View File

@ -22,7 +22,8 @@
{% include 'attention_bar.html' %}
{% include 'post_form.html' %}
{% if config.blotter %}<hr /><div class="blotter">{{ config.blotter }}</div>{% endif %}
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
<hr />
<form name="postcontrols" action="{{ config.post_url }}" method="post">
<input type="hidden" name="board" value="{{ board.uri }}" />

View File

@ -38,7 +38,7 @@
{% include 'boardlist.html' %}
{% endif %}
{% if config.blotter %}<hr /><div class="blotter">{{ config.blotter }}</div>{% endif %}
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
<hr />
<form name="postcontrols" action="{{ config.post_url }}" method="post">
<input type="hidden" name="board" value="{{ board.uri }}" />

View File

@ -14,14 +14,14 @@
<td>{{ test.name }}</td>
<td class="minimal" style="text-align:center">
{% if test.result %}
<i style="color:#090" class="icon-check-sign"></i>
<i style="font-size:11pt;color:#090" class="icon-check-sign"></i>
{% else %}
{% if test.required %}
{% set errors = errors + 1 %}
<i style="color:#d00" class="icon-exclamation-sign"></i>
<i style="font-size:11pt;color:#d00" class="icon-exclamation-sign"></i>
{% else %}
{% set warnings = warnings + 1 %}
<i style="color:#f80" class="icon-warning-sign"></i>
<i style="font-size:11pt;color:#f80" class="icon-warning-sign"></i>
{% endif %}
{% endif %}
</td>
@ -34,9 +34,9 @@
{% for test in tests if not test.result%}
<li style="margin-bottom:5px">
{% if test.required %}
<i style="color:#d00" class="icon-exclamation-sign"></i> <strong>Error:</strong>
<i style="font-size:11pt;color:#d00" class="icon-exclamation-sign"></i> <strong>Error:</strong>
{% else %}
<i style="color:#f80" class="icon-warning-sign"></i> <strong>Warning:</strong>
<i style="font-size:11pt;color:#f80" class="icon-warning-sign"></i> <strong>Warning:</strong>
{% endif %}
{{ test.message }}
</li>

View File

@ -0,0 +1,95 @@
<form action="?step=3" method="post" autocomplete="off">
<fieldset>
<legend>Database (MySQL)</legend>
<label for="db_server">Server:</label>
<input type="text" id="db_server" name="db[server]" value="{{ config.db.server }}">
<label for="db_db">Database:</label>
<input type="text" id="db_db" name="db[database]" value="{{ config.db.database }}">
<label for="db_prefix">Table prefix (optional):</label>
<input type="text" id="db_prefix" name="db[prefix]" value="{{ config.db.prefix }}">
<label for="db_user">Username:</label>
<input type="text" id="db_user" name="db[user]" value="{{ config.db.user }}">
<label for="db_pass">Password:</label>
<input type="password" id="db_pass" name="db[password]" value="">
</fieldset>
<p style="text-align:center" class="unimportant">The following is all later configurable. For more options, <a href="http://tinyboard.org/docs/?p=Config">edit your configuration files</a> after installing.</p>
<fieldset>
<legend>Cookies</legend>
<label for="cookies_mod">Moderator cookie:</label>
<input type="text" id="cookies_mod" name="cookies[mod]" value="{{ config.cookies.mod }}">
<label for="cookies_salt">Secure salt:</label>
<input type="text" id="cookies_salt" name="cookies[salt]" value="{{ config.cookies.salt }}" size="40">
</fieldset>
<fieldset>
<legend>Flood control</legend>
<label for="flood_time">Seconds before each post:</label>
<input type="text" id="flood_time" name="flood_time" value="{{ config.flood_time }}">
<label for="flood_time_ip">Seconds before you can repost something (post the exact same text):</label>
<input type="text" id="flood_time_ip" name="flood_time_ip" value="{{ config.flood_time_ip }}">
<label for="flood_time_same">Same as above, but with a different IP address:</label>
<input type="text" id="flood_time_same" name="flood_time_same" value="{{ config.flood_time_same }}">
<label for="max_body">Maximum post body length:</label>
<input type="text" id="max_body" name="max_body" value="{{ config.max_body }}">
<label for="reply_limit">Replies in a thread before it can no longer be bumped:</label>
<input type="text" id="reply_limit" name="reply_limit" value="{{ config.reply_limit }}">
<label for="max_links">Maximum number of links in a single post:</label>
<input type="text" id="max_links" name="max_links" value="{{ config.max_links }}">
</fieldset>
<fieldset>
<legend>Images</legend>
<label for="max_filesize">Maximum image filesize (bytes):</label>
<input type="text" id="max_filesize" name="max_filesize" value="{{ config.max_filesize }}">
<label for="thumb_width">Thumbnail width:</label>
<input type="text" id="thumb_width" name="thumb_width" value="{{ config.thumb_width }}">
<label for="thumb_height">Thumbnail height:</label>
<input type="text" id="thumb_height" name="thumb_height" value="{{ config.thumb_height }}">
<label for="max_width">Maximum image width:</label>
<input type="text" id="max_width" name="max_width" value="{{ config.max_width }}">
<label for="max_height">Maximum image height:</label>
<input type="text" id="max_height" name="max_height" value="{{ config.max_height }}">
</fieldset>
<fieldset>
<legend>Display</legend>
<label for="threads_per_page">Threads per page:</label>
<input type="text" id="threads_per_page" name="threads_per_page" value="{{ config.threads_per_page }}">
<label for="max_pages">Page limit:</label>
<input type="text" id="max_pages" name="max_pages" value="{{ config.max_pages }}">
<label for="threads_preview">Number of replies to show per thread on the index page:</label>
<input type="text" id="threads_preview" name="threads_preview" value="{{ config.threads_preview }}">
</fieldset>
<fieldset>
<legend>Directories</legend>
<label for="root">Root URI (include trailing slash):</label>
<input type="text" id="root" name="root" value="{{ config.root }}" size="40">
</fieldset>
<fieldset>
<legend>Miscellaneous</legend>
<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">
</fieldset>
<p style="text-align:center">
<input type="submit" value="Complete installation">
</p>
</form>

View File

@ -1,17 +1,35 @@
{% if readonly %}
<p style="text-align:center;max-width:800px;margin:20px auto">Tinyboard does not have the required permissions to edit <strong>inc/instance-config.php</strong>. To make changes, you will need to change the file's permissions first or manually edit the code.</p>
{% endif %}
{% if not readonly %}<form method="post" action="">{% endif %}
<textarea name="code" id="code" style="display:block; margin:auto;width:100%;max-width:800px;height:500px{% if readonly %};background:#eee" readonly{% else %}"{% endif %}>
{{ php }}
</textarea>
<div style="max-width:800px;margin:auto">
<p>
Any changes you make here will simply be appended to <code>{{ file }}</code>. If you wish to make the most of Tinyboard's customizability, you can instead edit the file directly. This page is intended for making quick changes and for those who don't have a basic understanding of PHP code.
</p>
{% if boards|count %}
<ul>
{% if board %}
<li><a href="?/config">Edit site-wide config</a></li>
{% endif %}
{% for _board in boards if _board.uri != board %}
<li>
<a href="?/config/{{ _board.uri }}">Edit config for {{ config.board_abbreviation|sprintf(_board.uri) }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
<ul style="padding:0;text-align:center;list-style:none">
<li><input name="save" type="submit" value="{% trans 'Save changes' %}"{% if readonly %} disabled{% endif %}></li>
</ul>
{% if not readonly %}</form>{% endif %}
{% if readonly %}
<p>Tinyboard does not have the required permissions to edit <code>{{ file }}</code>. To make changes, you will need to change the file's permissions first or manually edit the code.</p>
{% endif %}
{% if not readonly %}<form method="post" action="">{% endif %}
<textarea name="code" id="code" style="margin:auto;width:100%;height:500px{% if readonly %};background:#eee" readonly{% else %}"{% endif %}>
{{ php }}
</textarea>
<ul style="padding:0;text-align:center;list-style:none">
<li><input name="save" type="submit" value="{% trans 'Save changes' %}"{% if readonly %} disabled{% endif %}></li>
</ul>
{% if not readonly %}</form>{% endif %}
</div>
<script type="text/javascript">
var observe;

View File

@ -1,10 +1,25 @@
<p>
Any changes you make here will simply be appended to <code>{{ file }}</code>. If you wish to make the most of Tinyboard's customizability, you can instead edit the file directly. This page is intended for making quick changes and for those who don't have a basic understanding of PHP code.
</p>
{% if boards|count %}
<ul>
{% if board %}
<li><a href="?/config">Edit site-wide config</a></li>
{% endif %}
{% for _board in boards if _board.uri != board %}
<li>
<a href="?/config/{{ _board.uri }}">Edit config for {{ config.board_abbreviation|sprintf(_board.uri) }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
<form method="post" action="">
<table class="mod config-editor">
<tr>
<th class="minimal">Name</th>
<th>Value</th>
<th class="minimal">Type</th>
<th>Description</th>
<th class="minimal">{% trans 'Name' %}</th>
<th>{% trans 'Value' %}</th>
<th class="minimal">{% trans 'Type' %}</th>
<th>{% trans 'Description' %}</th>
</tr>
{% for var in conf if var.type != 'array' %}
{% if var.name|count == 1 %}
@ -25,7 +40,7 @@
<td>
{% if var.type == 'string' %}
<input name="{{ name }}" type="text" value="{{ var.value|e }}">
{% elseif var.type == 'integer' and var.name.0 == 'mod' and (var.default in ['JANITOR', 'MOD', 'ADMIN', 'DISABLED'] or var.default|slice(0, 14) == "$config['mod']") and var.value <= constant('DISABLED') %}
{% elseif var.permissions %}
<select name="{{ name }}">
<option value="{{ constant('JANITOR') }}"{% if var.value == constant('JANITOR')%} selected{% endif %}>JANITOR</option>
<option value="{{ constant('MOD') }}"{% if var.value == constant('MOD')%} selected{% endif %}>MOD</option>
@ -49,8 +64,8 @@
{{ var.type|e }}
</td>
<td>
{{ var.comment|join('<br>') }}
<td style="word-wrap:break-word;width:50%">
{{ var.comment|join(' ') }}
</td>
</tr>
{% endfor %}

View File

@ -4,6 +4,9 @@
{{ antibot.html() }}
<input type="hidden" name="board" value="{{ board.uri }}">
{{ antibot.html() }}
{% if current_page %}
<input type="hidden" name="page" value="{{ current_page }}">
{% endif %}
{% if mod %}<input type="hidden" name="mod" value="1">{% endif %}
<table>
{% if not config.field_disable_name or (mod and post.mod|hasPermission(config.mod.bypass_field_disable, board.uri)) %}<tr>

View File

@ -30,7 +30,7 @@
{% include 'post_form.html' %}
{% if config.blotter %}<hr /><div class="blotter">{{ config.blotter }}</div>{% endif %}
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
<hr />
<form name="postcontrols" action="{{ config.post_url }}" method="post">
<input type="hidden" name="board" value="{{ board.uri }}" />