Browse Source

Merge branch 'master' of https://github.com/savetheinternet/Tinyboard

Conflicts:
	inc/config.php
	inc/functions.php
	templates/generic_page.html
	templates/index.html
pull/40/head
czaks 11 years ago
parent
commit
a0855cdcf5
  1. 1043
      inc/config.php
  2. 12
      inc/database.php
  3. 70
      inc/display.php
  4. 4
      inc/filters.php
  5. 628
      inc/functions.php
  6. 28
      inc/image.php
  7. 53
      inc/mod/config-editor.php
  8. 73
      inc/mod/pages.php
  9. 184
      install.php
  10. 84
      mod.php
  11. 11
      post.php
  12. 9
      stylesheets/gentoochan.css
  13. 2
      stylesheets/ricechan.css
  14. 18
      templates/error.html
  15. 3
      templates/generic_page.html
  16. 2
      templates/index.html
  17. 10
      templates/installer/check-requirements.html
  18. 95
      templates/installer/config.html
  19. 44
      templates/mod/config-editor-php.html
  20. 29
      templates/mod/config-editor.html
  21. 3
      templates/post_form.html
  22. 2
      templates/thread.html

1043
inc/config.php

File diff suppressed because it is too large

12
inc/database.php

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

70
inc/display.php

@ -57,8 +57,8 @@ function createBoardlist($mod=false) {
); );
} }
function error($message, $priority = true) { function error($message, $priority = true, $debug_stuff = false) {
global $board, $mod, $config; global $board, $mod, $config, $db_error;
if ($config['syslog'] && $priority !== false) { if ($config['syslog'] && $priority !== false) {
// Use LOG_NOTICE instead of LOG_ERR or LOG_WARNING because most error message are not significant. // 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"); 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( die(Element('page.html', array(
'config'=>$config, 'config' => $config,
'title'=>_('Error'), 'title' => _('Error'),
'subtitle'=>_('An error has occured.'), 'subtitle' => _('An error has occured.'),
'body'=>'<center>' . 'body' => Element('error.html', array(
'<h2>' . _($message) . '</h2>' . 'config' => $config,
(isset($board) ? 'message' => $message,
"<p><a href=\"" . $config['root'] . 'mod' => $mod,
($mod ? $config['file_mod'] . '?/' : '') . 'board' => isset($board) ? $board : false,
$board['dir'] . $config['file_index'] . "\">"._("Go back")."</a>.</p>" : '') . 'debug' => is_array($debug_stuff) ? str_replace("\n", '&#10;', utf8tohtml(print_r($debug_stuff, true))) : utf8tohtml($debug_stuff)
'</center>' ))
))); )));
} }
@ -319,31 +323,31 @@ class Post {
// Delete // Delete
if (hasPermission($config['mod']['delete'], $board['uri'], $this->mod)) 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 // Delete all posts by IP
if (hasPermission($config['mod']['deletebyip'], $board['uri'], $this->mod)) 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) // Delete all posts by IP (global)
if (hasPermission($config['mod']['deletebyip_global'], $board['uri'], $this->mod)) 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 // Ban
if (hasPermission($config['mod']['ban'], $board['uri'], $this->mod)) 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 // Ban & Delete
if (hasPermission($config['mod']['bandelete'], $board['uri'], $this->mod)) 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) // Delete file (keep post)
if (!empty($this->file) && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod)) 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 // Edit post
if (hasPermission($config['mod']['editpost'], $board['uri'], $this->mod)) 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)) if (!empty($built))
$built = '<span class="controls">' . $built . '</span>'; $built = '<span class="controls">' . $built . '</span>';
@ -420,54 +424,54 @@ class Thread {
// Mod controls (on posts) // Mod controls (on posts)
// Delete // Delete
if (hasPermission($config['mod']['delete'], $board['uri'], $this->mod)) 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 // Delete all posts by IP
if (hasPermission($config['mod']['deletebyip'], $board['uri'], $this->mod)) 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) // Delete all posts by IP (global)
if (hasPermission($config['mod']['deletebyip_global'], $board['uri'], $this->mod)) 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 // Ban
if (hasPermission($config['mod']['ban'], $board['uri'], $this->mod)) 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 // Ban & Delete
if (hasPermission($config['mod']['bandelete'], $board['uri'], $this->mod)) 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) // Delete file (keep post)
if (!empty($this->file) && $this->file != 'deleted' && hasPermission($config['mod']['deletefile'], $board['uri'], $this->mod)) 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 // Sticky
if (hasPermission($config['mod']['sticky'], $board['uri'], $this->mod)) if (hasPermission($config['mod']['sticky'], $board['uri'], $this->mod))
if ($this->sticky) 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 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 (hasPermission($config['mod']['bumplock'], $board['uri'], $this->mod))
if ($this->bumplocked) 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 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 // Lock
if (hasPermission($config['mod']['lock'], $board['uri'], $this->mod)) if (hasPermission($config['mod']['lock'], $board['uri'], $this->mod))
if ($this->locked) 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 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)) 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 // Edit post
if (hasPermission($config['mod']['editpost'], $board['uri'], $this->mod)) 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)) if (!empty($built))
$built = '<span class="controls op">' . $built . '</span>'; $built = '<span class="controls op">' . $built . '</span>';

4
inc/filters.php

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

628
inc/functions.php

File diff suppressed because it is too large

28
inc/image.php

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

53
inc/mod/config-editor.php

@ -13,13 +13,14 @@ function config_vars() {
'default_temp' => false 'default_temp' => false
); );
$temp_comment = false; $temp_comment = false;
$line_no = 0;
foreach ($config_file as $line) { foreach ($config_file as $line) {
if ($temp_comment) { if ($temp_comment) {
$var['comment'][] = $temp_comment; $var['comment'][] = $temp_comment;
$temp_comment = false; $temp_comment = false;
} }
if (preg_match('!^\s*// (.*)$!', $line, $matches)) { if (preg_match('!^\s*// ([^$].*)$!', $line, $matches)) {
if ($var['default'] !== false) { if ($var['default'] !== false) {
$line = ''; $line = '';
$temp_comment = $matches[1]; $temp_comment = $matches[1];
@ -28,7 +29,10 @@ function config_vars() {
} }
} else if ($var['default_temp'] !== false) { } else if ($var['default_temp'] !== false) {
$var['default_temp'] .= "\n" . $line; $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]); $var['name'] = explode('][', $matches[1]);
if (count($var['name']) == 1) { if (count($var['name']) == 1) {
$var['name'] = preg_replace('/^\'(.*)\'$/', '$1', end($var['name'])); $var['name'] = preg_replace('/^\'(.*)\'$/', '$1', end($var['name']));
@ -43,20 +47,37 @@ function config_vars() {
$var['default_temp'] = $matches[2]; $var['default_temp'] = $matches[2];
} }
if (trim($line) === '') { if ($var['name'] !== false) {
if ($var['name'] !== false) { if ($var['default_temp'])
if ($var['default_temp']) $var['default'] = $var['default_temp'];
$var['default'] = $var['default_temp']; if ($var['default'][0] == '&')
continue; // This is just an alias.
$temp = eval('return ' . $var['default'] . ';'); if (!preg_match('/^array|\[\]|function/', $var['default']) && !preg_match('/^Example: /', trim(implode(' ', $var['comment'])))) {
if (!isset($temp)) $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'; $var['type'] = 'unknown';
else } else {
$var['type'] = gettype($temp); $var['type'] = gettype($temp);
}
unset($var['default_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')))) { 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; $conf[] = $var;
} }
} }
@ -65,9 +86,17 @@ function config_vars() {
'name' => false, 'name' => false,
'comment' => array(), 'comment' => array(),
'default' => false, 'default' => false,
'default_temp' => false 'default_temp' => false,
'commented' => false,
'permissions' => false,
); );
} }
if (trim($line) === '') {
$var['comment'] = array();
}
$line_no++;
} }
return $conf; return $conf;

73
inc/mod/pages.php

@ -800,21 +800,21 @@ function mod_page_ip($ip) {
$args['token'] = make_secure_link_token('ban'); $args['token'] = make_secure_link_token('ban');
if (hasPermission($config['mod']['view_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->bindValue(':ip', $ip);
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
$args['bans'] = $query->fetchAll(PDO::FETCH_ASSOC); $args['bans'] = $query->fetchAll(PDO::FETCH_ASSOC);
} }
if (hasPermission($config['mod']['view_notes'])) { 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->bindValue(':ip', $ip);
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
$args['notes'] = $query->fetchAll(PDO::FETCH_ASSOC); $args['notes'] = $query->fetchAll(PDO::FETCH_ASSOC);
} }
if (hasPermission($config['mod']['modlog_ip'])) { 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->bindValue(':search', '%' . $ip . '%');
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
$args['logs'] = $query->fetchAll(PDO::FETCH_ASSOC); $args['logs'] = $query->fetchAll(PDO::FETCH_ASSOC);
@ -1941,30 +1941,43 @@ function mod_report_dismiss($id, $all = false) {
} }
function mod_config() { function mod_config($board_config = false) {
global $config, $mod; global $config, $mod, $board;
if ($board_config && !openBoard($board_config))
error($config['error']['noboard']);
if (!hasPermission($config['mod']['edit_config'])) if (!hasPermission($config['mod']['edit_config'], $board_config))
error($config['error']['noaccess']); error($config['error']['noaccess']);
$config_file = $board_config ? $board['dir'] . 'config.php' : 'inc/instance-config.php';
if ($config['mod']['config_editor_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'])) { if (!$readonly && isset($_POST['code'])) {
$code = $_POST['code']; $code = $_POST['code'];
file_put_contents('inc/instance-config.php', $code); file_put_contents($config_file, $code);
header('Location: ?/config', true, $config['redirect_http']); header('Location: ?/config' . ($board_config ? '/' . $board_config : ''), true, $config['redirect_http']);
return; 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)); $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; return;
} }
require_once 'inc/mod/config-editor.php'; require_once 'inc/mod/config-editor.php';
$conf = config_vars(); $conf = config_vars();
@ -1975,7 +1988,7 @@ function mod_config() {
foreach ($var['name'] as $n) foreach ($var['name'] as $n)
$c = &$c[$n]; $c = &$c[$n];
} else { } else {
$c = $config[$var['name']]; $c = @$config[$var['name']];
} }
$var['value'] = $c; $var['value'] = $c;
@ -2010,14 +2023,28 @@ function mod_config() {
$config_append .= '[' . var_export($var['name'], true) . ']'; $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)) { if (!empty($config_append)) {
$config_append = "\n// Changes made via web editor by \"" . $mod['username'] . "\" @ " . date('r') . ":\n" . $config_append . "\n"; $config_append = "\n// Changes made via web editor by \"" . $mod['username'] . "\" @ " . date('r') . ":\n" . $config_append . "\n";
if (!is_file($config_file))
if (!@file_put_contents('inc/instance-config.php', $config_append, FILE_APPEND)) { $config_append = "<?php\n\n$config_append";
if (!@file_put_contents($config_file, $config_append, FILE_APPEND)) {
$config_append = htmlentities($config_append); $config_append = htmlentities($config_append);
if ($config['minify_html']) if ($config['minify_html'])
@ -2026,8 +2053,8 @@ function mod_config() {
$page['title'] = 'Cannot write to file!'; $page['title'] = 'Cannot write to file!';
$page['config'] = $config; $page['config'] = $config;
$page['body'] = ' $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">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>inc/instance-config.php</strong>:</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> <textarea style="width:700px;height:370px;margin:auto;display:block;background:white;color:black" readonly>' . $config_append . '</textarea>
'; ';
echo Element('page.html', $page); 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; 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() { function mod_themes_list() {

184
install.php

@ -459,7 +459,7 @@ if ($step == 0) {
array( array(
'category' => 'Database', 'category' => 'Database',
'name' => 'MySQL PDO driver installed', '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, 'required' => true,
'message' => 'The required <a href="http://www.php.net/manual/en/ref.pdo-mysql.php">PDO MySQL driver</a> is not installed.', '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( array(
'category' => 'Image processing', 'category' => 'Image processing',
'name' => 'Imagick extension installed', 'name' => 'Imagick extension installed',
'result' => extension_loaded('imagick1'), 'result' => extension_loaded('imagick'),
'required' => false, '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.', '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( array(
'category' => 'Image processing', 'category' => 'Image processing',
'name' => '`gifsicle` (command-line animted GIF thumbnailing)', 'name' => '`gifsicle` (command-line animted GIF thumbnailing)',
'result' => $can_exec && shell_exec('which gifsicle1'), '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.',
), ),
@ -532,6 +532,13 @@ if ($step == 0) {
'result' => is_writable('inc/instance-config.php'), 'result' => is_writable('inc/instance-config.php'),
'required' => false, '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.' '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 // Basic config
$page['title'] = 'Configuration'; $page['title'] = 'Configuration';
function create_salt() { $config['cookies']['salt'] = substr(base64_encode(sha1(rand())), 0, 30);
return substr(base64_encode(sha1(rand())), 0, rand(25, 31)); $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> echo Element('page.html', array(
<legend>Directories</legend> 'body' => Element('installer/config.html', array(
<label for="root">Root URI (include trailing slash):</label> 'config' => $config
<input type="text" id="root" name="root" value="' . $config['root'] . '" /> )),
'title' => 'Configuration',
<label for="dir_img">Image directory:</label> 'config' => $config
<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);
} elseif ($step == 3) { } elseif ($step == 3) {
$instance_config = $instance_config =
'<?php '<?php
@ -771,7 +627,7 @@ if ($step == 0) {
sql_open(); sql_open();
if (mysql_version() < 50503) 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 // This code is probably horrible, but what I'm trying
// to do is find all of the SQL queires and put them // to do is find all of the SQL queires and put them

84
mod.php

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

11
post.php

@ -218,7 +218,7 @@ if (isset($_POST['delete'])) {
} }
if (!$post['mod']) { 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) if ($post['antispam_hash'] === true)
error($config['error']['spam']); error($config['error']['spam']);
} }
@ -430,7 +430,7 @@ if (isset($_POST['delete'])) {
$post['filehash'] = $config['file_hash']($upload); $post['filehash'] = $config['file_hash']($upload);
$post['filesize'] = filesize($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 // 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)) {
@ -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'])) { if (!$post['op'] && strtolower($post['email']) != 'sage' && !$thread['sage'] && ($config['reply_limit'] == 0 || $numposts['replies']+1 < $config['reply_limit'])) {
bumpThread($post['thread']); 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']) if ($post['op'])
clean(); clean();

9
stylesheets/gentoochan.css

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

2
stylesheets/ricechan.css

@ -95,7 +95,7 @@ form table tr th {
background: #7E3737 !important; background: #7E3737 !important;
color: #B99B9B !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; border: 2px solid rgba(94, 50, 50, 0.56) !important;
background: #7C7373 !important; background: #7C7373 !important;
color: #311313 !important; color: #311313 !important;

18
templates/error.html

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

3
templates/generic_page.html

@ -22,7 +22,8 @@
{% include 'attention_bar.html' %} {% include 'attention_bar.html' %}
{% include 'post_form.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 /> <hr />
<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 }}" />

2
templates/index.html

@ -38,7 +38,7 @@
{% include 'boardlist.html' %} {% include 'boardlist.html' %}
{% endif %} {% 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 /> <hr />
<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 }}" />

10
templates/installer/check-requirements.html

@ -14,14 +14,14 @@
<td>{{ test.name }}</td> <td>{{ test.name }}</td>
<td class="minimal" style="text-align:center"> <td class="minimal" style="text-align:center">
{% if test.result %} {% 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 %} {% else %}
{% if test.required %} {% if test.required %}
{% set errors = errors + 1 %} {% 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 %} {% else %}
{% set warnings = warnings + 1 %} {% 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 %}
{% endif %} {% endif %}
</td> </td>
@ -34,9 +34,9 @@
{% for test in tests if not test.result%} {% for test in tests if not test.result%}
<li style="margin-bottom:5px"> <li style="margin-bottom:5px">
{% if test.required %} {% 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 %} {% 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 %} {% endif %}
{{ test.message }} {{ test.message }}
</li> </li>

95
templates/installer/config.html

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

44
templates/mod/config-editor-php.html

@ -1,17 +1,35 @@
{% if readonly %} <div style="max-width:800px;margin:auto">
<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> <p>
{% endif %} 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 %}
{% if not readonly %}<form method="post" action="">{% endif %} <ul>
<textarea name="code" id="code" style="display:block; margin:auto;width:100%;max-width:800px;height:500px{% if readonly %};background:#eee" readonly{% else %}"{% endif %}> {% if board %}
{{ php }} <li><a href="?/config">Edit site-wide config</a></li>
</textarea> {% 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 %}
{% 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 %}
<ul style="padding:0;text-align:center;list-style:none"> {% if not readonly %}<form method="post" action="">{% endif %}
<li><input name="save" type="submit" value="{% trans 'Save changes' %}"{% if readonly %} disabled{% endif %}></li> <textarea name="code" id="code" style="margin:auto;width:100%;height:500px{% if readonly %};background:#eee" readonly{% else %}"{% endif %}>
</ul> {{ php }}
{% if not readonly %}</form>{% endif %} </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"> <script type="text/javascript">
var observe; var observe;

29
templates/mod/config-editor.html

@ -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=""> <form method="post" action="">
<table class="mod config-editor"> <table class="mod config-editor">
<tr> <tr>
<th class="minimal">Name</th> <th class="minimal">{% trans 'Name' %}</th>
<th>Value</th> <th>{% trans 'Value' %}</th>
<th class="minimal">Type</th> <th class="minimal">{% trans 'Type' %}</th>
<th>Description</th> <th>{% trans 'Description' %}</th>
</tr> </tr>
{% for var in conf if var.type != 'array' %} {% for var in conf if var.type != 'array' %}
{% if var.name|count == 1 %} {% if var.name|count == 1 %}
@ -25,7 +40,7 @@
<td> <td>
{% if var.type == 'string' %} {% if var.type == 'string' %}
<input name="{{ name }}" type="text" value="{{ var.value|e }}"> <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 }}"> <select name="{{ name }}">
<option value="{{ constant('JANITOR') }}"{% if var.value == constant('JANITOR')%} selected{% endif %}>JANITOR</option> <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> <option value="{{ constant('MOD') }}"{% if var.value == constant('MOD')%} selected{% endif %}>MOD</option>
@ -49,8 +64,8 @@
{{ var.type|e }} {{ var.type|e }}
</td> </td>
<td> <td style="word-wrap:break-word;width:50%">
{{ var.comment|join('<br>') }} {{ var.comment|join(' ') }}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

3
templates/post_form.html

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

2
templates/thread.html

@ -30,7 +30,7 @@
{% include 'post_form.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 /> <hr />
<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 }}" />

Loading…
Cancel
Save