Browse Source
Conflicts: inc/lib/Twig/Extensions/Extension/Tinyboard.php install.php mod.php stylesheets/style.css templates/index.html templates/page.html templates/thread.htmlpull/40/head
Michael Save
12 years ago
59 changed files with 7313 additions and 3525 deletions
@ -0,0 +1,287 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* Copyright (c) 2010-2012 Tinyboard Development Group |
||||
|
*/ |
||||
|
|
||||
|
if (realpath($_SERVER['SCRIPT_FILENAME']) == str_replace('\\', '/', __FILE__)) { |
||||
|
// You cannot request this file directly. |
||||
|
exit; |
||||
|
} |
||||
|
|
||||
|
// create a hash/salt pair for validate logins |
||||
|
function mkhash($username, $password, $salt = false) { |
||||
|
global $config; |
||||
|
|
||||
|
if (!$salt) { |
||||
|
// create some sort of salt for the hash |
||||
|
$salt = substr(base64_encode(sha1(rand() . time(), true) . $config['cookies']['salt']), 0, 15); |
||||
|
|
||||
|
$generated_salt = true; |
||||
|
} |
||||
|
|
||||
|
// generate hash (method is not important as long as it's strong) |
||||
|
$hash = substr(base64_encode(md5($username . sha1($username . $password . $salt . ($config['mod']['lock_ip'] ? $_SERVER['REMOTE_ADDR'] : ''), true), true)), 0, 20); |
||||
|
|
||||
|
if (isset($generated_salt)) |
||||
|
return Array($hash, $salt); |
||||
|
else |
||||
|
return $hash; |
||||
|
} |
||||
|
|
||||
|
function login($username, $password, $makehash=true) { |
||||
|
global $mod; |
||||
|
|
||||
|
// SHA1 password |
||||
|
if ($makehash) { |
||||
|
$password = sha1($password); |
||||
|
} |
||||
|
|
||||
|
$query = prepare("SELECT `id`,`type`,`boards` FROM `mods` WHERE `username` = :username AND `password` = :password LIMIT 1"); |
||||
|
$query->bindValue(':username', $username); |
||||
|
$query->bindValue(':password', $password); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
if ($user = $query->fetch()) { |
||||
|
return $mod = Array( |
||||
|
'id' => $user['id'], |
||||
|
'type' => $user['type'], |
||||
|
'username' => $username, |
||||
|
'hash' => mkhash($username, $password), |
||||
|
'boards' => explode(',', $user['boards']) |
||||
|
); |
||||
|
} else return false; |
||||
|
} |
||||
|
|
||||
|
function setCookies() { |
||||
|
global $mod, $config; |
||||
|
if (!$mod) |
||||
|
error('setCookies() was called for a non-moderator!'); |
||||
|
|
||||
|
setcookie($config['cookies']['mod'], |
||||
|
$mod['username'] . // username |
||||
|
':' . |
||||
|
$mod['hash'][0] . // password |
||||
|
':' . |
||||
|
$mod['hash'][1], // salt |
||||
|
time() + $config['cookies']['expire'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, false, true); |
||||
|
} |
||||
|
|
||||
|
function destroyCookies() { |
||||
|
global $config; |
||||
|
// Delete the cookies |
||||
|
setcookie($config['cookies']['mod'], 'deleted', time() - $config['cookies']['expire'], $config['cookies']['jail']?$config['cookies']['path'] : '/', null, false, true); |
||||
|
} |
||||
|
|
||||
|
function create_pm_header() { |
||||
|
global $mod; |
||||
|
$query = prepare("SELECT `id` FROM `pms` WHERE `to` = :id AND `unread` = 1"); |
||||
|
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
if ($pm = $query->fetch()) { |
||||
|
return Array('id' => $pm['id'], 'waiting' => $query->rowCount() - 1); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
function modLog($action, $_board=null) { |
||||
|
global $mod, $board, $config; |
||||
|
$query = prepare("INSERT INTO `modlogs` VALUES (:id, :ip, :board, :time, :text)"); |
||||
|
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT); |
||||
|
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); |
||||
|
$query->bindValue(':time', time(), PDO::PARAM_INT); |
||||
|
$query->bindValue(':text', $action); |
||||
|
if (isset($_board)) |
||||
|
$query->bindValue(':board', $_board); |
||||
|
elseif (isset($board)) |
||||
|
$query->bindValue(':board', $board['uri']); |
||||
|
else |
||||
|
$query->bindValue(':board', null, PDO::PARAM_NULL); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
if ($config['syslog']) |
||||
|
_syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action); |
||||
|
} |
||||
|
|
||||
|
// Generates a <ul> element with a list of linked |
||||
|
// boards and their subtitles. (without the <ul> opening and ending tags) |
||||
|
function ulBoards() { |
||||
|
global $mod, $config; |
||||
|
|
||||
|
$body = ''; |
||||
|
|
||||
|
// List of boards |
||||
|
$boards = listBoards(); |
||||
|
|
||||
|
foreach ($boards as &$b) { |
||||
|
$body .= '<li>' . |
||||
|
'<a href="?/' . |
||||
|
sprintf($config['board_path'], $b['uri']) . $config['file_index'] . |
||||
|
'">' . |
||||
|
sprintf($config['board_abbreviation'], $b['uri']) . |
||||
|
'</a> - ' . |
||||
|
$b['title'] . |
||||
|
(isset($b['subtitle']) ? '<span class="unimportant"> — ' . $b['subtitle'] . '</span>' : '') . |
||||
|
($mod['type'] >= $config['mod']['manageboards'] ? |
||||
|
' <a href="?/' . $b['uri'] . '/edit" class="unimportant">[manage]</a>' : '') . |
||||
|
'</li>'; |
||||
|
} |
||||
|
|
||||
|
if ($mod['type'] >= $config['mod']['newboard']) { |
||||
|
$body .= '<li style="margin-top:15px;"><a href="?/new"><strong>' . _('Create new board') . '</strong></a></li>'; |
||||
|
} |
||||
|
return $body; |
||||
|
} |
||||
|
|
||||
|
function form_newBan($ip=null, $reason='', $continue=false, $delete=false, $board=false, $allow_public = false) { |
||||
|
global $config, $mod; |
||||
|
|
||||
|
$boards = listBoards(); |
||||
|
$__boards = '<li><input type="radio" checked="checked" name="board" id="board_*" value=""/> <label style="display:inline" for="board_*"><em>' . _('all boards') . '</em></label></li>'; |
||||
|
foreach ($boards as &$_board) { |
||||
|
$__boards .= '<li>' . |
||||
|
'<input type="radio" name="board" id="board_' . $_board['uri'] . '" value="' . $_board['uri'] . '">' . |
||||
|
'<label style="display:inline" for="board_' . $_board['uri'] . '"> ' . |
||||
|
($_board['uri'] == '*' ? |
||||
|
'<em>"*"</em>' |
||||
|
: |
||||
|
sprintf($config['board_abbreviation'], $_board['uri']) |
||||
|
) . |
||||
|
' - ' . $_board['title'] . |
||||
|
'</label>' . |
||||
|
'</li>'; |
||||
|
} |
||||
|
|
||||
|
return '<fieldset><legend>New ban</legend>' . |
||||
|
'<form action="?/ban" method="post">' . |
||||
|
($continue ? '<input type="hidden" name="continue" value="' . htmlentities($continue) . '" />' : '') . |
||||
|
($delete || $allow_public ? '<input type="hidden" name="' . (!$allow_public ? 'delete' : 'post') . '" value="' . htmlentities($delete) . '" />' : '') . |
||||
|
($board ? '<input type="hidden" name="board" value="' . htmlentities($board) . '" />' : '') . |
||||
|
'<table>' . |
||||
|
'<tr>' . |
||||
|
'<th><label for="ip">IP ' . |
||||
|
($config['ban_cidr'] ? '<span class="unimportant">(or subnet)' : '') . |
||||
|
'</span></label></th>' . |
||||
|
'<td><input type="text" name="ip" id="ip" size="30" maxlength="30" ' . |
||||
|
(isset($ip) ? |
||||
|
'value="' . htmlentities($ip) . '" ' : '' |
||||
|
) . |
||||
|
'/></td>' . |
||||
|
'</tr>' . |
||||
|
'<tr>' . |
||||
|
'<th><label for="reason">Reason</label></th>' . |
||||
|
'<td><textarea name="reason" id="reason" rows="5" cols="30">' . |
||||
|
htmlentities($reason) . |
||||
|
'</textarea></td>' . |
||||
|
'</tr>' . |
||||
|
($mod['type'] >= $config['mod']['public_ban'] && $allow_public ? |
||||
|
'<tr>' . |
||||
|
'<th><label for="message">Message</label></th>' . |
||||
|
'<td><input type="checkbox" id="public_message" name="public_message"/>' . |
||||
|
' <input type="text" name="message" id="message" size="35" maxlength="200" value="' . htmlentities($config['mod']['default_ban_message']) . '" />' . |
||||
|
' <span class="unimportant">(public; attached to post)</span></td>' . |
||||
|
'<script type="text/javascript">' . |
||||
|
'document.getElementById(\'message\').disabled = true;' . |
||||
|
'document.getElementById(\'public_message\').onchange = function() {' . |
||||
|
'document.getElementById(\'message\').disabled = !this.checked;' . |
||||
|
'}' . |
||||
|
|
||||
|
'</script>' . |
||||
|
'</tr>' |
||||
|
: '') . |
||||
|
'<tr>' . |
||||
|
'<th><label for="length">Length</label></th>' . |
||||
|
'<td><input type="text" name="length" id="length" size="20" maxlength="40" />' . |
||||
|
' <span class="unimportant">(eg. "2d1h30m" or "2 days")</span></td>' . |
||||
|
'</tr>' . |
||||
|
|
||||
|
'<tr>' . |
||||
|
'<th>Board</th>' . |
||||
|
'<td><ul style="list-style:none;padding:2px 5px">' . $__boards . '</tl></td>' . |
||||
|
'</tr>' . |
||||
|
|
||||
|
'<tr>' . |
||||
|
'<td></td>' . |
||||
|
'<td><input name="new_ban" type="submit" value="New Ban" /></td>' . |
||||
|
'</tr>' . |
||||
|
'</table>' . |
||||
|
'</form>' . |
||||
|
'</fieldset>'; |
||||
|
} |
||||
|
|
||||
|
function form_newBoard() { |
||||
|
return '<fieldset><legend>New board</legend>' . |
||||
|
'<form action="?/new" method="post">' . |
||||
|
'<table>' . |
||||
|
'<tr>' . |
||||
|
'<th><label for="board">URI</label></th>' . |
||||
|
'<td><input type="text" name="uri" id="board" size="10" />' . |
||||
|
' <span class="unimportant">(eg. "b"; "mu")</span></td>' . |
||||
|
'</tr>' . |
||||
|
'<tr>' . |
||||
|
'<th><label for="title">Title</label></th>' . |
||||
|
'<td><input type="text" name="title" id="title" size="25" />' . |
||||
|
' <span class="unimportant">(eg. "Random")</span></td>' . |
||||
|
'</tr>' . |
||||
|
'<tr>' . |
||||
|
'<th><label for="subtitle">Subtitle</label></th>' . |
||||
|
'<td><input type="text" name="subtitle" id="subtitle" size="25" />' . |
||||
|
' <span class="unimportant">(optional)</span></td>' . |
||||
|
'</tr>' . |
||||
|
'<tr>' . |
||||
|
'<td></td>' . |
||||
|
'<td><input name="new_board" type="submit" value="New Board" /></td>' . |
||||
|
'</tr>' . |
||||
|
'</table>' . |
||||
|
'</form>' . |
||||
|
'</fieldset>'; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function removeBan($id) { |
||||
|
global $config, $memcached; |
||||
|
|
||||
|
$query = prepare("DELETE FROM `bans` WHERE `id` = :id"); |
||||
|
$query->bindValue(':id', $id, PDO::PARAM_INT); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
//if ($config['memcached']['enabled']) { |
||||
|
// Remove cached ban |
||||
|
// TODO |
||||
|
// $memcached->delete("ban_{$id}"); |
||||
|
//} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Validate session |
||||
|
|
||||
|
if (isset($_COOKIE[$config['cookies']['mod']])) { |
||||
|
// Should be username:hash:salt |
||||
|
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]); |
||||
|
if (count($cookie) != 3) { |
||||
|
destroyCookies(); |
||||
|
error($config['error']['malformed']); |
||||
|
} |
||||
|
|
||||
|
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM `mods` WHERE `username` = :username LIMIT 1"); |
||||
|
$query->bindValue(':username', $cookie[0]); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
$user = $query->fetch(); |
||||
|
|
||||
|
// validate password hash |
||||
|
if ($cookie[1] != mkhash($cookie[0], $user['password'], $cookie[2])) { |
||||
|
// Malformed cookies |
||||
|
destroyCookies(); |
||||
|
error($config['error']['malformed']); |
||||
|
} |
||||
|
|
||||
|
$mod = Array( |
||||
|
'id' => $user['id'], |
||||
|
'type' => $user['type'], |
||||
|
'username' => $cookie[0], |
||||
|
'boards' => explode(',', $user['boards']) |
||||
|
); |
||||
|
} |
||||
|
|
@ -0,0 +1,152 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* Copyright (c) 2010-2012 Tinyboard Development Group |
||||
|
*/ |
||||
|
|
||||
|
if (realpath($_SERVER['SCRIPT_FILENAME']) == str_replace('\\', '/', __FILE__)) { |
||||
|
// You cannot request this file directly. |
||||
|
exit; |
||||
|
} |
||||
|
|
||||
|
// create a hash/salt pair for validate logins |
||||
|
function mkhash($username, $password, $salt = false) { |
||||
|
global $config; |
||||
|
|
||||
|
if (!$salt) { |
||||
|
// create some sort of salt for the hash |
||||
|
$salt = substr(base64_encode(sha1(rand() . time(), true) . $config['cookies']['salt']), 0, 15); |
||||
|
|
||||
|
$generated_salt = true; |
||||
|
} |
||||
|
|
||||
|
// generate hash (method is not important as long as it's strong) |
||||
|
$hash = substr(base64_encode(md5($username . sha1($username . $password . $salt . ($config['mod']['lock_ip'] ? $_SERVER['REMOTE_ADDR'] : ''), true), true)), 0, 20); |
||||
|
|
||||
|
if (isset($generated_salt)) |
||||
|
return Array($hash, $salt); |
||||
|
else |
||||
|
return $hash; |
||||
|
} |
||||
|
|
||||
|
function login($username, $password, $makehash=true) { |
||||
|
global $mod; |
||||
|
|
||||
|
// SHA1 password |
||||
|
if ($makehash) { |
||||
|
$password = sha1($password); |
||||
|
} |
||||
|
|
||||
|
$query = prepare("SELECT `id`,`type`,`boards` FROM `mods` WHERE `username` = :username AND `password` = :password LIMIT 1"); |
||||
|
$query->bindValue(':username', $username); |
||||
|
$query->bindValue(':password', $password); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
if ($user = $query->fetch()) { |
||||
|
return $mod = Array( |
||||
|
'id' => $user['id'], |
||||
|
'type' => $user['type'], |
||||
|
'username' => $username, |
||||
|
'hash' => mkhash($username, $password), |
||||
|
'boards' => explode(',', $user['boards']) |
||||
|
); |
||||
|
} else return false; |
||||
|
} |
||||
|
|
||||
|
function setCookies() { |
||||
|
global $mod, $config; |
||||
|
if (!$mod) |
||||
|
error('setCookies() was called for a non-moderator!'); |
||||
|
|
||||
|
setcookie($config['cookies']['mod'], |
||||
|
$mod['username'] . // username |
||||
|
':' . |
||||
|
$mod['hash'][0] . // password |
||||
|
':' . |
||||
|
$mod['hash'][1], // salt |
||||
|
time() + $config['cookies']['expire'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, false, true); |
||||
|
} |
||||
|
|
||||
|
function destroyCookies() { |
||||
|
global $config; |
||||
|
// Delete the cookies |
||||
|
setcookie($config['cookies']['mod'], 'deleted', time() - $config['cookies']['expire'], $config['cookies']['jail']?$config['cookies']['path'] : '/', null, false, true); |
||||
|
} |
||||
|
|
||||
|
function modLog($action, $_board=null) { |
||||
|
global $mod, $board, $config; |
||||
|
$query = prepare("INSERT INTO `modlogs` VALUES (:id, :ip, :board, :time, :text)"); |
||||
|
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT); |
||||
|
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); |
||||
|
$query->bindValue(':time', time(), PDO::PARAM_INT); |
||||
|
$query->bindValue(':text', $action); |
||||
|
if (isset($_board)) |
||||
|
$query->bindValue(':board', $_board); |
||||
|
elseif (isset($board)) |
||||
|
$query->bindValue(':board', $board['uri']); |
||||
|
else |
||||
|
$query->bindValue(':board', null, PDO::PARAM_NULL); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
if ($config['syslog']) |
||||
|
_syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action); |
||||
|
} |
||||
|
|
||||
|
// Validate session |
||||
|
|
||||
|
if (isset($_COOKIE[$config['cookies']['mod']])) { |
||||
|
// Should be username:hash:salt |
||||
|
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]); |
||||
|
if (count($cookie) != 3) { |
||||
|
destroyCookies(); |
||||
|
error($config['error']['malformed']); |
||||
|
} |
||||
|
|
||||
|
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM `mods` WHERE `username` = :username LIMIT 1"); |
||||
|
$query->bindValue(':username', $cookie[0]); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
$user = $query->fetch(); |
||||
|
|
||||
|
// validate password hash |
||||
|
if ($cookie[1] != mkhash($cookie[0], $user['password'], $cookie[2])) { |
||||
|
// Malformed cookies |
||||
|
destroyCookies(); |
||||
|
error($config['error']['malformed']); |
||||
|
} |
||||
|
|
||||
|
$mod = Array( |
||||
|
'id' => $user['id'], |
||||
|
'type' => $user['type'], |
||||
|
'username' => $cookie[0], |
||||
|
'boards' => explode(',', $user['boards']) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function create_pm_header() { |
||||
|
global $mod, $config; |
||||
|
|
||||
|
if ($config['cache']['enabled'] && ($header = cache::get('pm_unread_' . $mod['id'])) !== false) { |
||||
|
if ($header === true) |
||||
|
return false; |
||||
|
|
||||
|
return $header; |
||||
|
} |
||||
|
|
||||
|
$query = prepare("SELECT `id` FROM `pms` WHERE `to` = :id AND `unread` = 1"); |
||||
|
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
if ($pm = $query->fetch()) |
||||
|
$header = Array('id' => $pm['id'], 'waiting' => $query->rowCount() - 1); |
||||
|
else |
||||
|
$header = true; |
||||
|
|
||||
|
if ($config['cache']['enabled']) |
||||
|
cache::set('pm_unread_' . $mod['id'], $header); |
||||
|
|
||||
|
if ($header === true) |
||||
|
return false; |
||||
|
|
||||
|
return $header; |
||||
|
} |
||||
|
|
@ -0,0 +1,95 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* Copyright (c) 2010-2012 Tinyboard Development Group |
||||
|
*/ |
||||
|
|
||||
|
if (realpath($_SERVER['SCRIPT_FILENAME']) == str_replace('\\', '/', __FILE__)) { |
||||
|
// You cannot request this file directly. |
||||
|
exit; |
||||
|
} |
||||
|
|
||||
|
function parse_time($str) { |
||||
|
if (empty($str)) |
||||
|
return false; |
||||
|
|
||||
|
if (($time = @strtotime($str)) !== false) |
||||
|
return $time; |
||||
|
|
||||
|
if (!preg_match('/^((\d+)\s?ye?a?r?s?)?\s?+((\d+)\s?mon?t?h?s?)?\s?+((\d+)\s?we?e?k?s?)?\s?+((\d+)\s?da?y?s?)?((\d+)\s?ho?u?r?s?)?\s?+((\d+)\s?mi?n?u?t?e?s?)?\s?+((\d+)\s?se?c?o?n?d?s?)?$/', $str, $matches)) |
||||
|
return false; |
||||
|
|
||||
|
$expire = 0; |
||||
|
|
||||
|
if (isset($matches[2])) { |
||||
|
// Years |
||||
|
$expire += $matches[2]*60*60*24*365; |
||||
|
} |
||||
|
if (isset($matches[4])) { |
||||
|
// Months |
||||
|
$expire += $matches[4]*60*60*24*30; |
||||
|
} |
||||
|
if (isset($matches[6])) { |
||||
|
// Weeks |
||||
|
$expire += $matches[6]*60*60*24*7; |
||||
|
} |
||||
|
if (isset($matches[8])) { |
||||
|
// Days |
||||
|
$expire += $matches[8]*60*60*24; |
||||
|
} |
||||
|
if (isset($matches[10])) { |
||||
|
// Hours |
||||
|
$expire += $matches[10]*60*60; |
||||
|
} |
||||
|
if (isset($matches[12])) { |
||||
|
// Minutes |
||||
|
$expire += $matches[12]*60; |
||||
|
} |
||||
|
if (isset($matches[14])) { |
||||
|
// Seconds |
||||
|
$expire += $matches[14]; |
||||
|
} |
||||
|
|
||||
|
return time() + $expire; |
||||
|
} |
||||
|
|
||||
|
function ban($mask, $reason, $length, $board) { |
||||
|
global $mod, $pdo; |
||||
|
|
||||
|
$query = prepare("INSERT INTO `bans` VALUES (NULL, :ip, :mod, :time, :expires, :reason, :board)"); |
||||
|
$query->bindValue(':ip', $mask); |
||||
|
$query->bindValue(':mod', $mod['id']); |
||||
|
$query->bindValue(':time', time()); |
||||
|
if ($reason !== '') { |
||||
|
markup($reason); |
||||
|
$query->bindValue(':reason', $reason); |
||||
|
} else |
||||
|
$query->bindValue(':reason', null, PDO::PARAM_NULL); |
||||
|
|
||||
|
if ($length > 0) |
||||
|
$query->bindValue(':expires', $length); |
||||
|
else |
||||
|
$query->bindValue(':expires', null, PDO::PARAM_NULL); |
||||
|
|
||||
|
if ($board) |
||||
|
$query->bindValue(':board', $board); |
||||
|
else |
||||
|
$query->bindValue(':board', null, PDO::PARAM_NULL); |
||||
|
|
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
modLog('Created a new ' . |
||||
|
($length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent') . |
||||
|
' ban (<small>#' . $pdo->lastInsertId() . '</small>) for ' . |
||||
|
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : utf8tohtml($mask)) . |
||||
|
' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason')); |
||||
|
} |
||||
|
|
||||
|
function unban($id) { |
||||
|
$query = prepare("DELETE FROM `bans` WHERE `id` = :id"); |
||||
|
$query->bindValue(':id', $id); |
||||
|
$query->execute() or error(db_error($query)); |
||||
|
|
||||
|
modLog("Removed ban #{$id}"); |
||||
|
} |
||||
|
|
@ -0,0 +1,75 @@ |
|||||
|
<?php |
||||
|
|
||||
|
function config_vars() { |
||||
|
global $config; |
||||
|
|
||||
|
$config_file = file('inc/config.php', FILE_IGNORE_NEW_LINES); |
||||
|
$conf = array(); |
||||
|
|
||||
|
$var = array( |
||||
|
'name' => false, |
||||
|
'comment' => array(), |
||||
|
'default' => false, |
||||
|
'default_temp' => false |
||||
|
); |
||||
|
$temp_comment = false; |
||||
|
foreach ($config_file as $line) { |
||||
|
if ($temp_comment) { |
||||
|
$var['comment'][] = $temp_comment; |
||||
|
$temp_comment = false; |
||||
|
} |
||||
|
|
||||
|
if (preg_match('!^\s*// (.*)$!', $line, $matches)) { |
||||
|
if ($var['default'] !== false) { |
||||
|
$line = ''; |
||||
|
$temp_comment = $matches[1]; |
||||
|
} else { |
||||
|
$var['comment'][] = $matches[1]; |
||||
|
} |
||||
|
} else if ($var['default_temp'] !== false) { |
||||
|
$var['default_temp'] .= "\n" . $line; |
||||
|
} elseif (preg_match('!^\s*\$config\[(.+?)\] = (.+?)(;( //.+)?)?$!', $line, $matches)) { |
||||
|
$var['name'] = explode('][', $matches[1]); |
||||
|
if (count($var['name']) == 1) { |
||||
|
$var['name'] = preg_replace('/^\'(.*)\'$/', '$1', end($var['name'])); |
||||
|
} else { |
||||
|
foreach ($var['name'] as &$i) |
||||
|
$i = preg_replace('/^\'(.*)\'$/', '$1', $i); |
||||
|
} |
||||
|
|
||||
|
if (isset($matches[3])) |
||||
|
$var['default'] = $matches[2]; |
||||
|
else |
||||
|
$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)) |
||||
|
$var['type'] = 'unknown'; |
||||
|
else |
||||
|
$var['type'] = gettype($temp); |
||||
|
|
||||
|
unset($var['default_temp']); |
||||
|
|
||||
|
if (!is_array($var['name']) || (end($var['name']) != '' && !in_array(reset($var['name']), array('stylesheets')))) { |
||||
|
$conf[] = $var; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$var = array( |
||||
|
'name' => false, |
||||
|
'comment' => array(), |
||||
|
'default' => false, |
||||
|
'default_temp' => false |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $conf; |
||||
|
} |
||||
|
|
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,71 @@ |
|||||
|
<!doctype html> |
||||
|
<html> |
||||
|
<head> |
||||
|
{% block head %} |
||||
|
<link rel="stylesheet" media="screen" href="{{ config.url_stylesheet }}"> |
||||
|
{% if config.url_favicon %}<link rel="shortcut icon" href="{{ config.url_favicon }}">{% endif %} |
||||
|
<title>{{ board.url }} - {{ board.name }}</title> |
||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes"> |
||||
|
{% if config.meta_keywords %}<meta name="keywords" content="{{ config.meta_keywords }}">{% endif %} |
||||
|
{% if config.default_stylesheet.1 != '' %}<link rel="stylesheet" type="text/css" id="stylesheet" href="{{ config.uri_stylesheets }}{{ config.default_stylesheet.1 }}">{% endif %} |
||||
|
{% if not nojavascript %} |
||||
|
<script type="text/javascript" src="{{ config.url_javascript }}"></script> |
||||
|
{% if not config.additional_javascript_compile %} |
||||
|
{% for javascript in config.additional_javascript %}<script type="text/javascript" src="{{ config.additional_javascript_url }}{{ javascript }}"></script>{% endfor %} |
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
{% if config.recaptcha %}<style type="text/css">{% raw %} |
||||
|
.recaptcha_image_cell { |
||||
|
background: none !important; |
||||
|
} |
||||
|
table.recaptchatable { |
||||
|
border: none !important; |
||||
|
} |
||||
|
#recaptcha_logo, #recaptcha_tagline { |
||||
|
display: none; |
||||
|
float: right; |
||||
|
} |
||||
|
.recaptchatable a { |
||||
|
display: block; |
||||
|
} |
||||
|
{% endraw %}</style>{% endif %} |
||||
|
{% endblock %} |
||||
|
</head> |
||||
|
<body> |
||||
|
{{ boardlist.top }} |
||||
|
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %} |
||||
|
{% if config.url_banner %}<img class="banner" src="{{ config.url_banner }}" {% if config.banner_width or config.banner_height %}style="{% if config.banner_width %}width:{{ config.banner_width }}px{% endif %};{% if config.banner_width %}height:{{ config.banner_height }}px{% endif %}" {% endif %}alt="" />{% endif %} |
||||
|
<header> |
||||
|
<h1>{{ board.url }} - {{ board.name }}</h1> |
||||
|
<div class="subtitle"> |
||||
|
{% if board.title %} |
||||
|
{{ board.title|e }} |
||||
|
{% endif %} |
||||
|
{% if mod %}<p><a href="?/">{% trans %}Return to dashboard{% endtrans %}</a></p>{% endif %} |
||||
|
</div> |
||||
|
</header> |
||||
|
|
||||
|
{% include 'post_form.html' %} |
||||
|
|
||||
|
{% if config.blotter %}<hr /><div class="blotter">{{ config.blotter }}</div>{% endif %} |
||||
|
<hr /> |
||||
|
<form name="postcontrols" action="{{ config.post_url }}" method="post"> |
||||
|
<input type="hidden" name="board" value="{{ board.uri }}" /> |
||||
|
{% if mod %}<input type="hidden" name="mod" value="1" />{% endif %} |
||||
|
{{ body }} |
||||
|
{% include 'report_delete.html' %} |
||||
|
</form> |
||||
|
<div class="pages">{{ btn.prev }} {% for page in pages %} |
||||
|
[<a {% if page.selected %}class="selected"{% endif %}{% if not page.selected %}href="{{ page.link }}"{% endif %}>{{ page.num }}</a>]{% if loop.last %} {% endif %} |
||||
|
{% endfor %} {{ btn.next }}</div> |
||||
|
{{ boardlist.bottom }} |
||||
|
<footer> |
||||
|
<p class="unimportant" style="margin-top:20px;text-align:center;">Powered by <a href="http://tinyboard.org/">Tinyboard</a> {{ config.version }} | <a href="http://tinyboard.org/">Tinyboard</a> Copyright © 2010-2012 Tinyboard Development Group</p> |
||||
|
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %} |
||||
|
</footer> |
||||
|
<script type="text/javascript">{% raw %} |
||||
|
ready(); |
||||
|
{% endraw %}</script> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,91 @@ |
|||||
|
{% if post and board %} |
||||
|
{% set action = '?/' ~ board ~ '/ban/' ~ post %} |
||||
|
{% else %} |
||||
|
{% set action = '?/ban' %} |
||||
|
{% endif %} |
||||
|
|
||||
|
<form action="{{ action }}" method="post"> |
||||
|
{% if redirect %} |
||||
|
<input type="hidden" name="redirect" value="{{ redirect|e }}"> |
||||
|
{% endif %} |
||||
|
{% if post and board %} |
||||
|
<input type="hidden" name="delete" value="{% if delete %}1{% else %}0{% endif %}"> |
||||
|
{% endif %} |
||||
|
|
||||
|
<table> |
||||
|
|
||||
|
<tr> |
||||
|
<th> |
||||
|
<label for="ip">{% trans 'IP' %} <span class="unimportant">{% trans '(or subnet)' %}</span></label> |
||||
|
</th> |
||||
|
<td> |
||||
|
{% if not hide_ip %} |
||||
|
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip }}"> |
||||
|
{% else %} |
||||
|
<em>{% trans 'hidden' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th> |
||||
|
<label for="reason">{% trans 'Reason' %}</label> |
||||
|
</th> |
||||
|
<td> |
||||
|
<textarea name="reason" id="reason" rows="5" cols="30">{{ reason|e }}</textarea> |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% if post and board and not delete %} |
||||
|
<tr> |
||||
|
<th> |
||||
|
<label for="reason">{% trans 'Message' %}</label> |
||||
|
</th> |
||||
|
<td> |
||||
|
<input type="checkbox" id="public_message" name="public_message"> |
||||
|
<input type="text" name="message" id="message" size="35" maxlength="200" value="{{ config.mod.default_ban_message|e }}"> |
||||
|
<span class="unimportant">({% trans 'public; attached to post' %})</span> |
||||
|
<script type="text/javascript"> |
||||
|
document.getElementById('message').disabled = true; |
||||
|
document.getElementById('public_message').onchange = function() { |
||||
|
document.getElementById('message').disabled = !this.checked; |
||||
|
} |
||||
|
</script> |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endif %} |
||||
|
<tr> |
||||
|
<th> |
||||
|
<label for="length">{% trans 'Length' %}</label> |
||||
|
</th> |
||||
|
<td> |
||||
|
<input type="text" name="length" id="length" size="20" maxlength="40"> |
||||
|
<span class="unimportant">(eg. "2d1h30m" or "2 days")</span></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Board' %}</th> |
||||
|
<td> |
||||
|
<ul style="list-style:none;padding:2px 5px"> |
||||
|
<li> |
||||
|
<input type="radio" name="board" value="*" id="ban-allboards" checked> |
||||
|
<label style="display:inline" for="ban-allboards"> |
||||
|
<em>{% trans 'all boards' %}</em> |
||||
|
</label> |
||||
|
</li> |
||||
|
|
||||
|
{% for board in boards %} |
||||
|
<li> |
||||
|
<input type="radio" name="board" value="{{ board.uri }}" id="ban-board-{{ board.uri }}"> |
||||
|
<label style="display:inline" for="ban-board-{{ board.uri }}"> |
||||
|
{{ config.board_abbreviation|sprintf(board.uri) }} - {{ board.title|e }} |
||||
|
</label> |
||||
|
</li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td></td> |
||||
|
<td><input name="new_ban" type="submit" value="{% trans 'New Ban' %}"></td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</form> |
||||
|
|
@ -0,0 +1,95 @@ |
|||||
|
{% if bans|count == 0 %} |
||||
|
<p style="text-align:center" class="unimportant">({% trans 'There are no active bans.' %})</p> |
||||
|
{% else %} |
||||
|
<form action="" method="post"> |
||||
|
<table class="mod" style="width:100%"> |
||||
|
<tr> |
||||
|
<th>{% trans 'IP address/mask' %}</th> |
||||
|
<th>{% trans 'Reason' %}</th> |
||||
|
<th>{% trans 'Board' %}</th> |
||||
|
<th>{% trans 'Set' %}</th> |
||||
|
<th>{% trans 'Duration' %}</th> |
||||
|
<th>{% trans 'Expires' %}</th> |
||||
|
<th>{% trans 'Staff' %}</th> |
||||
|
</tr> |
||||
|
{% for ban in bans %} |
||||
|
<tr{% if ban.expires != 0 and ban.expires < time() %} style="text-decoration:line-through"{% endif %}> |
||||
|
<td style="white-space: nowrap"> |
||||
|
<input type="checkbox" name="ban_{{ ban.id }}"> |
||||
|
{% if ban.real_ip %} |
||||
|
<a href="?/IP/{{ ban.ip }}">{{ ban.ip }}</a> |
||||
|
{% else %} |
||||
|
{{ ban.ip|e }} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td> |
||||
|
{% if ban.reason %} |
||||
|
{{ ban.reason }} |
||||
|
{% else %} |
||||
|
- |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td style="white-space: nowrap"> |
||||
|
{% if ban.board %} |
||||
|
{{ config.board_abbreviation|sprintf(ban.board) }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'all boards' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td style="white-space: nowrap"> |
||||
|
<span title="{{ ban.set|date(config.post_date) }}"> |
||||
|
{{ ban.set|ago }} ago |
||||
|
</span> |
||||
|
</td> |
||||
|
<td style="white-space: nowrap"> |
||||
|
{% if ban.expires == 0 %} |
||||
|
- |
||||
|
{% else %} |
||||
|
{{ (ban.expires - ban.set + time()) | until }} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td style="white-space: nowrap"> |
||||
|
{% if ban.expires == 0 %} |
||||
|
<em>{% trans 'never' %}</em> |
||||
|
{% else %} |
||||
|
{{ ban.expires|date(config.post_date) }} |
||||
|
{% if ban.expires > time() %} |
||||
|
<small>(in {{ ban.expires|until }})</small> |
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td> |
||||
|
{% if ban.username %} |
||||
|
{% if mod|hasPermission(config.mod.view_banstaff) %} |
||||
|
<a href="?/new_PM/{{ ban.username|e }}">{{ ban.username|e }}</a> |
||||
|
{% else %} |
||||
|
{% if mod|hasPermission(config.mod.view_banquestionmark) %} |
||||
|
<em>?</em> |
||||
|
{% else %} |
||||
|
|
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
{% elseif ban.mod == -1 %} |
||||
|
<em>system</em> |
||||
|
{% else %} |
||||
|
<em>{% trans 'deleted?' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
|
||||
|
<p style="text-align:center"> |
||||
|
<input type="submit" name="unban" value="{% trans 'Unban selected' %}"> |
||||
|
</p> |
||||
|
</form> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if count > bans|count %} |
||||
|
<p class="unimportant" style="text-align:center;word-wrap:break-word"> |
||||
|
{% for i in range(0, (count - 1) / config.mod.modlog_page) %} |
||||
|
<a href="?/bans/{{ i + 1 }}">[{{ i + 1 }}]</a> |
||||
|
{% endfor %} |
||||
|
</p> |
||||
|
{% endif %} |
||||
|
|
@ -0,0 +1,44 @@ |
|||||
|
{% if new %} |
||||
|
{% set action = '?/new-board' %} |
||||
|
{% else %} |
||||
|
{% set action = '?/edit/' ~ board.uri %} |
||||
|
{% endif %} |
||||
|
|
||||
|
<form action="{{ action }}" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>{% trans 'URI' %}</th> |
||||
|
<td> |
||||
|
{% if not new %} |
||||
|
{{ config.board_abbreviation|sprintf(board.uri) }} |
||||
|
{% else %} |
||||
|
/<input size="10" maxlength="255" type="text" name="uri" autocomplete="off">/ |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Title' %}</th> |
||||
|
<td> |
||||
|
<input size="25" type="text" name="title" value="{{ board.title|e }}" autocomplete="off"> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Subtitle' %}</th> |
||||
|
<td> |
||||
|
<input size="25" type="text" name="subtitle" value="{{ board.subtitle|e }}" autocomplete="off"> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
|
||||
|
<ul style="padding:0;text-align:center;list-style:none"> |
||||
|
{% if new %} |
||||
|
<li><input type="submit" value="{% trans 'Create board' %}"></li> |
||||
|
{% else %} |
||||
|
<li><input type="submit" value="{% trans 'Save changes' %}"></li> |
||||
|
{% if mod|hasPermission(config.mod.deleteboard) %} |
||||
|
<li><input name="delete" onclick="return confirm('Are you sure you want to permanently delete this board?');" type="submit" value="{% trans 'Delete board' %}"></li> |
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
</ul> |
||||
|
</form> |
||||
|
|
@ -0,0 +1,56 @@ |
|||||
|
<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> |
||||
|
</tr> |
||||
|
{% for var in conf if var.type != 'array' %} |
||||
|
{% if var.name|count == 1 %} |
||||
|
{% set name = 'cf_' ~ var.name %} |
||||
|
{% else %} |
||||
|
{% set name = 'cf_' ~ var.name|join('/') %} |
||||
|
{% endif %} |
||||
|
|
||||
|
<tr> |
||||
|
<th class="minimal"> |
||||
|
{% if var.name|count == 1 %} |
||||
|
{{ var.name }} |
||||
|
{% else %} |
||||
|
{{ var.name|join(' → ') }} |
||||
|
{% endif %} |
||||
|
</th> |
||||
|
|
||||
|
<td> |
||||
|
{% if var.type == 'string' %} |
||||
|
<input name="{{ name }}" type="text" value="{{ var.value|e }}"> |
||||
|
{% elseif var.type == 'integer' %} |
||||
|
<input name="{{ name }}" type="number" value="{{ var.value|e }}"> |
||||
|
{% elseif var.type == 'boolean' %} |
||||
|
<input name="{{ name }}" type="checkbox" {% if var.value %}checked{% endif %}> |
||||
|
{% else %} |
||||
|
? |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if var.type == 'integer' or var.type == 'boolean' %} |
||||
|
<small>Default: <code>{{ var.default }}</code></small> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
|
||||
|
<td class="minimal"> |
||||
|
{{ var.type|e }} |
||||
|
</td> |
||||
|
|
||||
|
<td> |
||||
|
{{ var.comment|join('<br>') }} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
|
||||
|
<ul style="padding:0;text-align:center;list-style:none"> |
||||
|
<li><input name="save" type="submit" value="{% trans 'Save changes' %}"></li> |
||||
|
</ul> |
||||
|
</form> |
||||
|
|
@ -0,0 +1,7 @@ |
|||||
|
<p style="text-align:center;font-size:1.1em"> |
||||
|
{% trans 'Are you sure you want to do that?' %} <a href="?/{{ request }}">{% trans 'Click to proceed to' %} ?/{{ request }}</a>. |
||||
|
</p> |
||||
|
<p class="unimportant" style="text-align:center"> |
||||
|
{% trans 'You are seeing this message because we were unable to serve a confirmation dialog, probably due to Javascript being disabled.' %} |
||||
|
</p> |
||||
|
|
@ -0,0 +1,139 @@ |
|||||
|
<fieldset> |
||||
|
<legend>{% trans 'Boards' %}</legend> |
||||
|
|
||||
|
<ul> |
||||
|
{% for board in boards %} |
||||
|
<li> |
||||
|
<a href="?/{{ config.board_path|sprintf(board.uri) }}{{ config.file_index }}">{{ config.board_abbreviation|sprintf(board.uri) }}</a> |
||||
|
- |
||||
|
{{ board.title|e }} |
||||
|
{% if board.subtitle %} |
||||
|
<small>— {{ board.subtitle|e }}</small> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.manageboards) %} |
||||
|
<a href="?/edit/{{ board.uri }}"><small>[{% trans 'edit' %}]</small></a> |
||||
|
{% endif %} |
||||
|
</li> |
||||
|
{% endfor %} |
||||
|
|
||||
|
{% if mod|hasPermission(config.mod.newboard) %} |
||||
|
<li style="margin-top:15px"><a href="?/new-board"><strong>{% trans 'Create new board' %}</strong></a></li> |
||||
|
{% endif %} |
||||
|
</ul> |
||||
|
</fieldset> |
||||
|
|
||||
|
<fieldset> |
||||
|
<legend>{% trans 'Messages' %}</legend> |
||||
|
<ul> |
||||
|
{% if mod|hasPermission(config.mod.noticeboard) %} |
||||
|
{% if noticeboard|count > 0 %} |
||||
|
<li> |
||||
|
{% trans 'Noticeboard' %}: |
||||
|
<ul> |
||||
|
{% for post in noticeboard %} |
||||
|
<li> |
||||
|
<a href="?/noticeboard#{{ post.id }}"> |
||||
|
{% if post.subject %} |
||||
|
{{ post.subject|e }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'no subject' %}</em> |
||||
|
{% endif %} |
||||
|
</a> |
||||
|
<small class="unimportant"> |
||||
|
— by |
||||
|
{% if post.username %} |
||||
|
{{ post.username|e }} |
||||
|
{% else %} |
||||
|
<em>deleted?</em> |
||||
|
{% endif %} |
||||
|
at |
||||
|
{{ post.time|date(config.post_date) }} |
||||
|
</small> |
||||
|
</li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
</li> |
||||
|
{% endif %} |
||||
|
<li><a href="?/noticeboard">{% trans 'View all entries' %}</a></li> |
||||
|
{% endif %} |
||||
|
<li><a href="?/news">{% trans 'News' %}</a></li> |
||||
|
<li> |
||||
|
<a href="?/inbox"> |
||||
|
{% trans 'PM inbox' %} |
||||
|
{% if unread_pms > 0 %}<strong>{%endif %}({{ unread_pms }} unread){% if unread_pms > 0 %}</strong>{%endif %} |
||||
|
</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</fieldset> |
||||
|
|
||||
|
<fieldset> |
||||
|
<legend>{% trans 'Administration' %}</legend> |
||||
|
|
||||
|
<ul> |
||||
|
{% if mod|hasPermission(config.mod.reports) %} |
||||
|
<li> |
||||
|
{% if reports > 0 %}<strong>{% endif %} |
||||
|
<a href="?/reports">{% trans 'Report queue' %} ({{ reports }})</a> |
||||
|
{% if reports > 0 %}</strong>{% endif %} |
||||
|
</li> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.view_banlist) %} |
||||
|
<li><a href="?/bans">{% trans 'Ban list' %}</a></li> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.manageusers) %} |
||||
|
<li><a href="?/users">{% trans 'Manage users' %}</a></li> |
||||
|
{% elseif mod|hasPermission(config.mod.change_password) %} |
||||
|
<li><a href="?/users/{{ mod.id }}">{% trans 'Change password' %}</a></li> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.themes) %} |
||||
|
<li><a href="?/themes">{% trans 'Manage themes' %}</a></li> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.modlog) %} |
||||
|
<li><a href="?/log">{% trans 'Moderation log' %}</a></li> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.rebuild) %} |
||||
|
<li><a href="?/rebuild">{% trans 'Rebuild' %}</a></li> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.show_config) %} |
||||
|
<li><a href="?/config">{% trans 'Configuration' %}</a></li> |
||||
|
{% endif %} |
||||
|
|
||||
|
</ul> |
||||
|
</fieldset> |
||||
|
|
||||
|
<fieldset> |
||||
|
<legend>{% trans 'Search' %}</legend> |
||||
|
|
||||
|
{# TODO #} |
||||
|
</fieldset> |
||||
|
|
||||
|
{% if config.debug %} |
||||
|
<fieldset> |
||||
|
<legend>{% trans 'Debug' %}</legend> |
||||
|
<ul> |
||||
|
<li><a href="?/debug/antispam">{% trans 'Anti-spam' %}</a></li> |
||||
|
</ul> |
||||
|
</fieldset> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if newer_release %} |
||||
|
<fieldset> |
||||
|
<legend>Update</legend> |
||||
|
<ul> |
||||
|
<li> |
||||
|
A newer version of Tinyboard |
||||
|
(<strong>v{{ newer_release.massive }}.{{ newer_release.major }}.{{ newer_release.minor }}</strong>) is available! |
||||
|
See <a href="http://tinyboard.org">http://tinyboard.org/</a> for upgrade instructions. |
||||
|
</li> |
||||
|
</ul> |
||||
|
</fieldset> |
||||
|
{% endif %} |
||||
|
|
||||
|
<fieldset> |
||||
|
<legend>{% trans 'User account' %}</legend> |
||||
|
|
||||
|
<ul> |
||||
|
<li><a href="?/logout">{% trans 'Logout' %}</a></li> |
||||
|
</ul> |
||||
|
</fieldset> |
||||
|
|
@ -0,0 +1,65 @@ |
|||||
|
<p style="text-align:center"> |
||||
|
Most used (in active): |
||||
|
</p> |
||||
|
<table class="modlog" style="width:700px;margin:auto"> |
||||
|
<tr> |
||||
|
<th>Board</th> |
||||
|
<th>Thread</th> |
||||
|
<th>Hash (SHA1)</th> |
||||
|
<th>Created</th> |
||||
|
<th>Expires</th> |
||||
|
<th>Passed</th> |
||||
|
</tr> |
||||
|
{% for hash in top %} |
||||
|
<tr> |
||||
|
<td>{{ config.board_abbreviation|sprintf(hash.board) }}</td> |
||||
|
<td> |
||||
|
{% if hash.thread %} |
||||
|
{{ hash.thread }} |
||||
|
{% else %} |
||||
|
- |
||||
|
{% endif %}</td> |
||||
|
<td> |
||||
|
<small><code>{{ hash.hash }}</code></small> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span title="{{ hash.created|date(config.post_date) }}">{{ hash.created|ago }} ago</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
{% if hash.expires %} |
||||
|
<span title="{{ hash.expires|date(config.post_date) }}"> |
||||
|
{{ hash.expires|until }} |
||||
|
</span> |
||||
|
{% else %} |
||||
|
- |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td>{{ hash.passed }}</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
<p style="text-align:center"> |
||||
|
Total: <strong>{{ total }}</strong> (<strong>{{ expiring }}</strong> set to expire) |
||||
|
</p> |
||||
|
|
||||
|
<form method="post" action="?/debug/antispam"> |
||||
|
<table class="modlog" style="width:1%;white-space:nowrap;margin:auto"> |
||||
|
<tr> |
||||
|
<th>Board</th> |
||||
|
<th>Thread</th> |
||||
|
<th></th> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<input type="text" name="board" style="width:90px" value="{{ board }}"> |
||||
|
</td> |
||||
|
<td> |
||||
|
<input type="text" name="thread" style="width:90px" value="{{ thread }}"> |
||||
|
</td> |
||||
|
<td> |
||||
|
<input type="submit" name="filter" value="Filter"> |
||||
|
<input type="submit" name="purge" value="Purge"> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</form> |
@ -0,0 +1,36 @@ |
|||||
|
{% if messages|count == 0 %} |
||||
|
<p style="text-align:center" class="unimportant">({% trans 'No private messages for you.' %})</p> |
||||
|
{% else %} |
||||
|
<table class="modlog"> |
||||
|
<tr> |
||||
|
<th>{% trans 'ID' %}</th> |
||||
|
<th>{% trans 'From' %}</th> |
||||
|
<th>{% trans 'Date' %}</th> |
||||
|
<th>{% trans 'Message snippet' %}</th> |
||||
|
</tr> |
||||
|
{% for message in messages %} |
||||
|
<tr{% if message.unread %} style="font-weight:bold"{% endif %}> |
||||
|
<td class="minimal"> |
||||
|
<a href="?/PM/{{ message.id }}"> |
||||
|
{{ message.id }} |
||||
|
</a> |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
{% if message.username %} |
||||
|
<a href="?/new_PM/{{ message.username|e }}">{{ message.username|e }}</a> |
||||
|
{% else %} |
||||
|
<em>{% trans 'deleted?' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
{{ message.time|date(config.post_date) }} |
||||
|
</td> |
||||
|
<td> |
||||
|
<a href="?/PM/{{ message.id }}"> |
||||
|
{{ message.snippet }} |
||||
|
</a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
{% endif %} |
@ -0,0 +1,47 @@ |
|||||
|
<table class="modlog"> |
||||
|
<tr> |
||||
|
<th>{% trans 'Staff' %}</th> |
||||
|
<th>{% trans 'IP address' %}</th> |
||||
|
<th>{% trans 'Time' %}</th> |
||||
|
<th>{% trans 'Board' %}</th> |
||||
|
<th>{% trans 'Action' %}</th> |
||||
|
</tr> |
||||
|
{% for log in logs %} |
||||
|
<tr> |
||||
|
<td class="minimal"> |
||||
|
{% if log.username %} |
||||
|
<a href="?/new_PM/{{ log.username|e }}">{{ log.username|e }}</a> |
||||
|
{% elseif log.mod == -1 %} |
||||
|
<em>system</em> |
||||
|
{% else %} |
||||
|
<em>{% trans 'deleted?' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
<a href="?/IP/{{ log.ip }}">{{ log.ip }}</a> |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
<span title="{{ log.time|date(config.post_date) }}">{{ log.time|ago }}</span> |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
{% if log.board %} |
||||
|
<a href="?/{{ config.board_path|sprintf(log.board) }}{{ config.file_index }}">{{ config.board_abbreviation|sprintf(log.board) }}</a> |
||||
|
{% else %} |
||||
|
- |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td> |
||||
|
{{ log.text }} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
|
||||
|
{% if count > logs|count %} |
||||
|
<p class="unimportant" style="text-align:center;word-wrap:break-word"> |
||||
|
{% for i in range(0, (count - 1) / config.mod.modlog_page) %} |
||||
|
<a href="?/log/{{ i + 1 }}">[{{ i + 1 }}]</a> |
||||
|
{% endfor %} |
||||
|
</p> |
||||
|
{% endif %} |
||||
|
|
@ -1,18 +1,17 @@ |
|||||
{% if error %}<h2 style="text-align:center">{{ error }}</h2>{% endif %} |
{% if error %}<h2 style="text-align:center">{{ error }}</h2>{% endif %} |
||||
<form action="" method="post"> |
<form action="" method="post"> |
||||
{% if redirect %}<input type="hidden" name="redirect" value="{{ redirect }}" />{% endif %} |
|
||||
<table style="margin-top:25px;"> |
<table style="margin-top:25px;"> |
||||
<tr> |
<tr> |
||||
<th> |
<th> |
||||
{% trans %}Username{% endtrans %} |
{% trans 'Username' %} |
||||
</th> |
</th> |
||||
<td> |
<td> |
||||
<input type="text" name="username" size="20" maxlength="30" value="{{ username }}"> |
<input type="text" name="username" size="20" maxlength="30" value="{{ username|e }}"> |
||||
</td> |
</td> |
||||
</tr> |
</tr> |
||||
<tr> |
<tr> |
||||
<th> |
<th> |
||||
{% trans %}Password{% endtrans %} |
{% trans 'Password' %} |
||||
</th> |
</th> |
||||
<td> |
<td> |
||||
<input type="password" name="password" size="20" maxlength="30" value=""> |
<input type="password" name="password" size="20" maxlength="30" value=""> |
@ -0,0 +1,41 @@ |
|||||
|
<form action="?/{{ board }}/move/{{ post }}" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th> |
||||
|
{% trans 'Thread ID' %} |
||||
|
</th> |
||||
|
<td> |
||||
|
>>>{{ config.board_abbreviation|sprintf(board) }}{{ post }} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th> |
||||
|
{% trans 'Leave shadow thread' %} |
||||
|
</th> |
||||
|
<td> |
||||
|
<input type="checkbox" name="shadow" checked> |
||||
|
<span class="unimportant">({% trans 'locks thread; replies to it with a link.' %})</span> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Target board' %}</th> |
||||
|
<td> |
||||
|
<ul style="list-style:none;padding:0"> |
||||
|
{% for targetboard in boards if targetboard.uri != board %} |
||||
|
<li> |
||||
|
<input type="radio" name="board" value="{{ targetboard.uri }}" id="ban-board-{{ targetboard.uri }}"> |
||||
|
<label style="display:inline" for="ban-board-{{ targetboard.uri }}"> |
||||
|
{{ config.board_abbreviation|sprintf(targetboard.uri) }} - {{ targetboard.title|e }} |
||||
|
</label> |
||||
|
</li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
|
||||
|
<ul style="padding:0;text-align:center;list-style:none"> |
||||
|
<li><input type="submit" value="{% trans 'Move thread' %}"></li> |
||||
|
</ul> |
||||
|
</form> |
||||
|
|
@ -0,0 +1,18 @@ |
|||||
|
<form action="?/new_PM/{{ username|e }}" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>To</th> |
||||
|
{% if mod|hasPermission(config.mod.editusers) %} |
||||
|
<td><a href="?/users/{{ id }}">{{ username|e }}</a></td> |
||||
|
{% else %} |
||||
|
<td>{{ username|e }}</td> |
||||
|
{% endif %} |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>Message</th> |
||||
|
<td><textarea name="message" rows="10" cols="40">{{ message }}</textarea></td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
|
||||
|
<p style="text-align:center"><input type="submit" value="{% trans 'Send message' %}"></p> |
||||
|
</form> |
@ -0,0 +1,70 @@ |
|||||
|
{% if mod|hasPermission(config.mod.news) %} |
||||
|
<fieldset> |
||||
|
<legend>{% trans 'New post' %}</legend> |
||||
|
<form style="margin:0" action="" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th> |
||||
|
{% if mod|hasPermission(config.mod.news_custom) %} |
||||
|
<label for="name">{% trans 'Name' %}</label> |
||||
|
{% else %} |
||||
|
{% trans 'Name' %} |
||||
|
{% endif %} |
||||
|
</th> |
||||
|
<td> |
||||
|
{% if mod|hasPermission(config.mod.news_custom) %} |
||||
|
<input type="text" size="55" name="name" id="name" value="{{ mod.username|e }}"> |
||||
|
{% else %} |
||||
|
{{ mod.username|e }} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th><label for="subject">{% trans 'Subject' %}</label></th> |
||||
|
<td><input type="text" size="55" name="subject" id="subject"></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th><label for="body">{% trans 'Body' %}</label></th> |
||||
|
<td><textarea name="body" id="body" style="width:100%;height:100px"></textarea></td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
<p style="text-align:center"> |
||||
|
<input type="submit" value="{% trans 'Post news entry' %}"> |
||||
|
</p> |
||||
|
</form> |
||||
|
</fieldset> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% for post in news %} |
||||
|
<div class="ban"> |
||||
|
{% if mod|hasPermission(config.mod.news_delete) %} |
||||
|
<span style="float:right;padding:2px"> |
||||
|
<a class="unimportant" href="?/news/delete/{{ post.id }}">[{% trans 'delete' %}]</a> |
||||
|
</span> |
||||
|
{% endif %} |
||||
|
<h2 id="{{ post.id }}"> |
||||
|
<small class="unimportant"> |
||||
|
<a href="#{{ post.id }}">#</a> |
||||
|
</small> |
||||
|
{% if post.subject %} |
||||
|
{{ post.subject|e }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'no subject' %}</em> |
||||
|
{% endif %} |
||||
|
<small class="unimportant"> |
||||
|
— {% trans 'by' %} {{ post.name }} {% trans 'at' %} {{ notice.time|date(config.post_date) }} |
||||
|
</small> |
||||
|
</h2> |
||||
|
<p> |
||||
|
{{ post.body }} |
||||
|
</p> |
||||
|
</div> |
||||
|
{% endfor %} |
||||
|
|
||||
|
{% if count > news|count %} |
||||
|
<p class="unimportant" style="text-align:center;word-wrap:break-word"> |
||||
|
{% for i in range(0, (count - 1) / config.mod.news_page) %} |
||||
|
<a href="?/news/{{ i + 1 }}">[{{ i + 1 }}]</a> |
||||
|
{% endfor %} |
||||
|
</p> |
||||
|
{% endif %} |
@ -0,0 +1,65 @@ |
|||||
|
{% if mod|hasPermission(config.mod.noticeboard_post) %} |
||||
|
<fieldset> |
||||
|
<legend>{% trans 'New post' %}</legend> |
||||
|
<form style="margin:0" action="" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>{% trans 'Name' %}</th> |
||||
|
<td>{{ mod.username|e }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th><label for="subject">{% trans 'Subject' %}</label></th> |
||||
|
<td><input type="text" size="55" name="subject" id="subject" /></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Body' %}</th> |
||||
|
<td><textarea name="body" style="width:100%;height:100px"></textarea></td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
<p style="text-align:center"> |
||||
|
<input type="submit" value="{% trans 'Post to noticeboard' %}" /> |
||||
|
</p> |
||||
|
</form> |
||||
|
</fieldset> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% for post in noticeboard %} |
||||
|
<div class="ban"> |
||||
|
{% if mod|hasPermission(config.mod.noticeboard_delete) %} |
||||
|
<span style="float:right;padding:2px"> |
||||
|
<a class="unimportant" href="?/noticeboard/delete/{{ post.id }}">[{% trans 'delete' %}]</a> |
||||
|
</span> |
||||
|
{% endif %} |
||||
|
<h2 id="{{ post.id }}"> |
||||
|
<small class="unimportant"> |
||||
|
<a href="#{{ post.id }}">#</a> |
||||
|
</small> |
||||
|
{% if post.subject %} |
||||
|
{{ post.subject|e }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'no subject' %}</em> |
||||
|
{% endif %} |
||||
|
<small class="unimportant"> |
||||
|
— {% trans 'by' %} |
||||
|
{% if post.username %} |
||||
|
{{ post.username|e }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'deleted?' %}</em> |
||||
|
{% endif %} |
||||
|
{% trans 'at' %} |
||||
|
{{ post.time|date(config.post_date) }} |
||||
|
</small> |
||||
|
</h2> |
||||
|
<p> |
||||
|
{{ post.body }} |
||||
|
</p> |
||||
|
</div> |
||||
|
{% endfor %} |
||||
|
|
||||
|
{% if count > noticeboard|count %} |
||||
|
<p class="unimportant" style="text-align:center;word-wrap:break-word"> |
||||
|
{% for i in range(0, (count - 1) / config.mod.noticeboard_page) %} |
||||
|
<a href="?/noticeboard/{{ i + 1 }}">[{{ i + 1 }}]</a> |
||||
|
{% endfor %} |
||||
|
</p> |
||||
|
{% endif %} |
@ -0,0 +1,44 @@ |
|||||
|
<form action="" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>{% trans 'From' %}</th> |
||||
|
{% if username %} |
||||
|
<td><a href="?/new_PM/{{ username|e }}">{{ username|e }}</a></td> |
||||
|
{% else %} |
||||
|
<td><em>{% trans 'deleted?' %}</em></td> |
||||
|
{% endif %} |
||||
|
</tr> |
||||
|
{% if to != mod.id %} |
||||
|
<tr> |
||||
|
<th>To</th> |
||||
|
{% if to_username %} |
||||
|
<td><a href="?/new_PM/{{ to_username|e }}">{{ to_username|e }}</a></td> |
||||
|
{% else %} |
||||
|
<td><em>{% trans 'deleted?' %}</em></td> |
||||
|
{% endif %} |
||||
|
</tr> |
||||
|
{% endif %} |
||||
|
<tr> |
||||
|
<th>{% trans 'Date' %}</th> |
||||
|
<td>{{ time|date(config.post_date) }} <small>({{ time|ago }} ago)</small></td> |
||||
|
|
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Message' %}</th> |
||||
|
<td>{{ message }}</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
|
||||
|
<ul style="list-style:none;text-align:center;padding:0"> |
||||
|
<li style="padding:5px 0"> |
||||
|
<input type="submit" name="delete" value="{% trans 'Delete forever' %}"> |
||||
|
</li> |
||||
|
{% if mod|hasPermission(config.mod.create_pm) %} |
||||
|
<li style="padding:5px 0"> |
||||
|
<a href="?/PM/{{ id }}/reply"> |
||||
|
{% trans 'Reply with quote' %} |
||||
|
</a> |
||||
|
</li> |
||||
|
{% endif %} |
||||
|
</ul> |
||||
|
</form> |
@ -0,0 +1,71 @@ |
|||||
|
<form style="width:300px;margin:auto" action="?/rebuild" method="post"> |
||||
|
<ul id="rebuild"> |
||||
|
<li style="margin-bottom:8px"> |
||||
|
<input type="checkbox" name="rebuild_all" id="rebuild_all" onchange="toggleall(this.checked)"> |
||||
|
<label for="rebuild_all"><strong>{% trans 'Toggle all' %}</strong></label> |
||||
|
<script> |
||||
|
function toggleall(val) { |
||||
|
/* TODO: something more suitable for all browsers? */ |
||||
|
var elements = document.getElementById('rebuild').querySelectorAll('input'); |
||||
|
for (i in elements) { |
||||
|
elements[i].checked = val; |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="checkbox" name="rebuild_cache" id="rebuild_cache" checked> |
||||
|
<label for="rebuild_cache">{% trans 'Flush cache' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="checkbox" name="rebuild_javascript" id="rebuild_javascript" checked> |
||||
|
<label for="rebuild_javascript">{% trans 'Rebuild Javascript' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="checkbox" name="rebuild_index" id="rebuild_index" checked> |
||||
|
<label for="rebuild_index">{% trans 'Rebuild index pages' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="checkbox" name="rebuild_thread" id="rebuild_thread" checked> |
||||
|
<label for="rebuild_thread">{% trans 'Rebuild thread pages' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="checkbox" name="rebuild_themes" id="rebuild_themes" checked> |
||||
|
<label for="rebuild_themes">{% trans 'Rebuild themes' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="checkbox" name="rebuild_posts" id="rebuild_posts"> |
||||
|
<label for="rebuild_posts">{% trans 'Rebuild replies' %}</label> |
||||
|
</li> |
||||
|
</ul> |
||||
|
|
||||
|
<hr> |
||||
|
|
||||
|
<ul id="boards"> |
||||
|
<li style="margin-bottom:8px"> |
||||
|
<input type="checkbox" name="boards_all" id="boards_all" onchange="toggleallboards(this.checked)" checked> |
||||
|
<label for="boards_all"><strong>{% trans 'All boards' %}</strong></label> |
||||
|
<script> |
||||
|
function toggleallboards(val) { |
||||
|
/* TODO: something more suitable for all browsers? */ |
||||
|
var elements = document.getElementById('boards').querySelectorAll('input'); |
||||
|
for (i in elements) { |
||||
|
elements[i].checked = val; |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
</li> |
||||
|
{% for board in boards %} |
||||
|
<li> |
||||
|
<input type="checkbox" name="board_{{ board.uri }}" id="board-{{ board.uri }}" checked> |
||||
|
<label for="board-{{ board.uri }}"> |
||||
|
{{ config.board_abbreviation|sprintf(board.uri) }} - {{ board.title|e }} |
||||
|
</label> |
||||
|
</li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
|
||||
|
<p style="text-align:center"> |
||||
|
<input type="submit" value="{% trans 'Rebuild' %}" name="rebuild"> |
||||
|
</p> |
||||
|
</form> |
@ -0,0 +1,12 @@ |
|||||
|
<div class="ban"> |
||||
|
<h2>{% trans 'Rebuilt' %}</h2> |
||||
|
<ul> |
||||
|
{% for log in logs %} |
||||
|
<li>{{ log }}</li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
<p> |
||||
|
<a href="?/rebuild">{% trans 'Go back and rebuild again' %}</a>. |
||||
|
</p> |
||||
|
</div> |
||||
|
|
@ -0,0 +1,26 @@ |
|||||
|
<div class="report"> |
||||
|
<hr> |
||||
|
{% trans 'Board' %}: <a href="?/{{ report.board }}/{{ config.file_index }}">{{ config.board_abbreviation|sprintf(report.board) }}</a> |
||||
|
<br> |
||||
|
{% trans 'Reason' %}: {{ report.reason }} |
||||
|
<br> |
||||
|
{% trans 'Report date' %}: {{ report.time|date(config.post_date) }} |
||||
|
<br> |
||||
|
{% if mod|hasPermission(config.mod.show_ip, report.board) %} |
||||
|
{% trans 'Reported by' %}: <a href="?/IP/{{ report.ip }}">{{ report.ip }}</a> |
||||
|
<br> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.report_dismiss, report.board) or mod|hasPermission(config.mod.report_dismiss_ip, report.board) %} |
||||
|
<hr> |
||||
|
{% if mod|hasPermission(config.mod.report_dismiss, report.board) %} |
||||
|
<a title="{% trans 'Discard abuse report' %}" href="?/reports/{{ report.id }}/dismiss">Dismiss</a> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.report_dismiss_ip, report.board) %} |
||||
|
{% if mod|hasPermission(config.mod.report_dismiss, report.board) %} |
||||
|
| |
||||
|
{% endif %} |
||||
|
<a title="{% trans 'Discard all abuse reports by this IP address' %}" href="?/reports/{{ report.id }}/dismissall">Dismiss+</a> |
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
|
@ -0,0 +1,6 @@ |
|||||
|
{% if reports %} |
||||
|
{{ reports }} |
||||
|
{% else %} |
||||
|
<p style="text-align:center" class="unimportant">({% trans 'There are no reports.' %})</p> |
||||
|
{% endif %} |
||||
|
|
@ -0,0 +1,27 @@ |
|||||
|
<form action="" method="post"> |
||||
|
{% if not config %} |
||||
|
<p style="text-align:center" class="unimportant">(No configuration required.)</p> |
||||
|
{% else %} |
||||
|
<table> |
||||
|
{% for conf in theme.config %} |
||||
|
<tr> |
||||
|
<th>{{ conf.title }}</th> |
||||
|
<td> |
||||
|
<input type="text" name="{{ conf.name }}" |
||||
|
{% if settings[conf.name] %}value="{{ settings[conf.name] }}"{% else %}{% if conf.default %}value="{{ conf.default }}"{% endif %}{% endif %} |
||||
|
{% if conf.size %} |
||||
|
size="{{ conf.size }}" |
||||
|
{% endif %} |
||||
|
/> |
||||
|
{% if conf.comment %} |
||||
|
<span class="unimportant">{{ conf.comment }}</span> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
{% endif %} |
||||
|
<p style="text-align:center"> |
||||
|
<input name="install" type="submit" value="{% trans 'Install theme' %}" /> |
||||
|
</p> |
||||
|
</form> |
@ -0,0 +1,9 @@ |
|||||
|
{% if message %} |
||||
|
<div style="border:1px dashed maroon;padding:20px;margin:auto;max-width:800px">{{ message }}</div> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if result %} |
||||
|
<p style="text-align:center">{% trans 'Successfully installed and built theme.' %}</p> |
||||
|
{% endif %} |
||||
|
|
||||
|
<p style="text-align:center"><a href="?/themes">{% trans 'Go back to themes' %}</a>.</p> |
@ -0,0 +1,2 @@ |
|||||
|
<p style="text-align:center">{{ 'Successfully rebuilt the <strong>%s</strong> theme.'|trans|sprintf(theme_name) }}</p> |
||||
|
<p style="text-align:center"><a href="?/themes">{% trans 'Go back to themes' %}</a>.</p> |
@ -0,0 +1,40 @@ |
|||||
|
{% if themes|count == 0 %} |
||||
|
<p style="text-align:center" class="unimportant">({% trans 'There are no themes available.' %})</p> |
||||
|
{% else %} |
||||
|
<table class="modlog"> |
||||
|
{% for theme_name, theme in themes %} |
||||
|
<tr> |
||||
|
<th class="minimal">{% trans 'Name' %}</th> |
||||
|
<td>{{ theme.name }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th class="minimal">{% trans 'Version' %}</th> |
||||
|
<td>{{ theme.version }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th class="minimal">{% trans 'Description' %}</th> |
||||
|
<td>{{ theme.description }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th class="minimal">{% trans 'Thumbnail' %}</th> |
||||
|
<td> |
||||
|
<img style="float:none;margin:4px{% if theme_name in themes_in_use %};border:2px solid red;padding:4px{% endif %}" src="{{ config.dir.themes_uri }}/{{ theme_name }}/thumb.png" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th class="minimal">{% trans 'Actions' %}</th> |
||||
|
<td><ul style="padding:0 20px"> |
||||
|
<li><a title=" {% trans 'Use theme' %}" href="?/themes/{{ theme_name }}"> |
||||
|
{% if theme_name in themes_in_use %}{% trans 'Reconfigure' %}{% else %}{% trans 'Install' %}{% endif %} |
||||
|
</a></li> |
||||
|
{% if theme_name in themes_in_use %} |
||||
|
<li><a href="?/themes/{{ theme_name }}/rebuild">{% trans 'Rebuild' %}</a></li> |
||||
|
<li><a href="?/themes/{{ theme_name }}/uninstall" onclick="return confirm('Are you sure you want to uninstall this theme?');">{% trans 'Uninstall' %}</a></li> |
||||
|
{% endif %} |
||||
|
</ul></td> |
||||
|
</tr> |
||||
|
<tr style="height:40px"><td colspan="2"><hr/></td></tr> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
{% endif %} |
@ -0,0 +1,125 @@ |
|||||
|
{% if new %} |
||||
|
{% set action = '?/users/new' %} |
||||
|
{% else %} |
||||
|
{% set action = '?/users/' ~ user.id %} |
||||
|
{% endif %} |
||||
|
|
||||
|
<form action="{{ action }}" method="post"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>{% trans 'Username' %}</th> |
||||
|
<td> |
||||
|
{% if new or mod|hasPermission(config.mod.editusers) %} |
||||
|
<input size="20" maxlength="30" type="text" name="username" value="{{ user.username|e }}" autocomplete="off"> |
||||
|
{% else %} |
||||
|
{{ user.username|e }} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Password' %}{% if not new %} <small style="font-weight:normal">({% trans 'new; optional' %})</small>{% endif %}</th> |
||||
|
<td> |
||||
|
{% if new or (mod|hasPermission(config.mod.editusers) or (mod|hasPermission(config.mod.change_password) and user.id == mod.id)) %} |
||||
|
<input size="20" maxlength="30" type="password" name="password" value="" autocomplete="off"> |
||||
|
{% else %} |
||||
|
- |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% if new %} |
||||
|
<tr> |
||||
|
<th>{% trans 'Class' %}</th> |
||||
|
<td> |
||||
|
<ul style="padding:5px 8px;list-style:none"> |
||||
|
<li> |
||||
|
<input type="radio" name="type" id="janitor" value="{{ constant('JANITOR') }}"> |
||||
|
<label for="janitor">{% trans 'Janitor' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="radio" name="type" id="mod" value="{{ constant('MOD') }}" checked> |
||||
|
<label for="mod">{% trans 'Mod' %}</label> |
||||
|
</li> |
||||
|
<li> |
||||
|
<input type="radio" name="type" id="admin" value="{{ constant('Admin') }}"> |
||||
|
<label for="admin">{% trans 'Admin' %}</label> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endif %} |
||||
|
<tr> |
||||
|
<th>{% trans 'Boards' %}</th> |
||||
|
<td> |
||||
|
<ul style="padding:0 5px;list-style:none"> |
||||
|
<li> |
||||
|
<input type="checkbox" id="allboards" name="allboards" |
||||
|
{% if '*' in user.boards %} checked{% endif %} |
||||
|
{% if not mod|hasPermission(config.mod.editusers) %} |
||||
|
disabled |
||||
|
{% endif %} |
||||
|
> |
||||
|
<label for="allboards">"*" - {% trans 'All boards' %}</label> |
||||
|
</li> |
||||
|
{% for board in boards %} |
||||
|
<li> |
||||
|
<input type="checkbox" id="board_{{ board.uri }}" name="board_{{ board.uri }}" |
||||
|
{% if board.uri in user.boards %} checked{% endif %} |
||||
|
{% if not mod|hasPermission(config.mod.editusers) %} |
||||
|
disabled |
||||
|
{% endif %} |
||||
|
> |
||||
|
<label for="board_{{ board.uri }}"> |
||||
|
{{ config.board_abbreviation|sprintf(board.uri) }} |
||||
|
- |
||||
|
{{ board.title|e }} |
||||
|
</label> |
||||
|
</li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
|
||||
|
<ul style="padding:0;text-align:center;list-style:none"> |
||||
|
{% if new %} |
||||
|
<li><input type="submit" value="{% trans 'Create user' %}"></li> |
||||
|
{% else %} |
||||
|
<li><input type="submit" value="{% trans 'Save changes' %}"></li> |
||||
|
{% if mod|hasPermission(config.mod.deleteusers) %} |
||||
|
<li><input name="delete" onclick="return confirm('Are you sure you want to permanently delete this user?');" type="submit" value="{% trans 'Delete user' %}"></li> |
||||
|
{% endif %} |
||||
|
{% endif %} |
||||
|
</ul> |
||||
|
</form> |
||||
|
|
||||
|
{% if logs|count > 0 %} |
||||
|
<table class="modlog" style="width:600px"> |
||||
|
<tr> |
||||
|
<th>{% trans 'IP address' %}</th> |
||||
|
<th>{% trans 'Time' %}</th> |
||||
|
<th>{% trans 'Board' %}</th> |
||||
|
<th>{% trans 'Action' %}</th> |
||||
|
</tr> |
||||
|
{% for log in logs %} |
||||
|
<tr> |
||||
|
<td class="minimal"> |
||||
|
<a href="?/IP/{{ log.ip }}">{{ log.ip }}</a> |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
<span title="{{ log.time|date(config.post_date) }}">{{ log.time|ago }}</span> |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
{% if log.board %} |
||||
|
<a href="?/{{ config.board_path|sprintf(log.board) }}{{ config.file_index }}">{{ config.board_abbreviation|sprintf(log.board) }}</a> |
||||
|
{% else %} |
||||
|
- |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td> |
||||
|
{{ log.text }} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
{% endif %} |
||||
|
|
@ -0,0 +1,71 @@ |
|||||
|
<table class="modlog" style="width:auto"> |
||||
|
<tr> |
||||
|
<th>{% trans 'ID' %}</th> |
||||
|
<th>{% trans 'Username' %}</th> |
||||
|
<th>{% trans 'Type' %}</th> |
||||
|
<th>{% trans 'Boards' %}</th> |
||||
|
{% if mod|hasPermission(config.mod.modlog) %} |
||||
|
<th>{% trans 'Last action' %}</th> |
||||
|
{% endif %} |
||||
|
<th>…</th> |
||||
|
</tr> |
||||
|
|
||||
|
{% for user in users %} |
||||
|
<tr> |
||||
|
<td><small>{{ user.id }}</small></td> |
||||
|
<td>{{ user.username|e }}</td> |
||||
|
<td> |
||||
|
{% if user.type == constant('JANITOR') %}{% trans 'Janitor' %} |
||||
|
{% elseif user.type == constant('MOD') %}{% trans 'Mod' %} |
||||
|
{% elseif user.type == constant('ADMIN') %}{% trans 'Admin' %} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td> |
||||
|
{% if user.boards == '' %} |
||||
|
<em>{% trans 'none' %}</em> |
||||
|
{% elseif user.boards == '*' %} |
||||
|
<em>{% trans 'all boards' %}</em> |
||||
|
{% else %} |
||||
|
{# This is really messy, but IMO it beats doing it in PHP. #} |
||||
|
{% set boards = user.boards|split(',') %} |
||||
|
{% set _boards = [] %} |
||||
|
{% for board in boards %} |
||||
|
{% set _boards = _boards|push(board == '*' ? '*' : config.board_abbreviation|sprintf(board)) %} |
||||
|
{% endfor %} |
||||
|
{% set _boards = _boards|sort %} |
||||
|
{{ _boards|join(', ') }} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
{% if mod|hasPermission(config.mod.modlog) %} |
||||
|
<td> |
||||
|
{% if user.last %} |
||||
|
<span title="{{ user.action|e }}">{{ user.last|ago }}</span> |
||||
|
{% else %} |
||||
|
<em>{% trans 'never' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
{% endif %} |
||||
|
<td> |
||||
|
{% if mod|hasPermission(config.mod.promoteusers) and user.type < constant('ADMIN') %} |
||||
|
<a style="float:left;text-decoration:none" href="?/users/{{ user.id }}/promote" title="{% trans 'Promote' %}">▲</a> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.promoteusers) and user.type > constant('JANITOR') %} |
||||
|
<a style="float:left;text-decoration:none" href="?/users/{{ user.id }}/demote" title="{% trans 'Demote' %}">▼</a> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.editusers) or (mod|hasPermission(config.mod.change_password) and mod.id == user.id) %} |
||||
|
<a class="unimportant" style="margin-left:5px;float:right" href="?/users/{{ user.id }}">[{% trans 'edit' %}]</a> |
||||
|
{% endif %} |
||||
|
{% if mod|hasPermission(config.mod.create_pm) %} |
||||
|
<a class="unimportant" style="margin-left:5px;float:right" href="?/new_PM/{{ user.username|e }}">[{% trans 'PM' %}]</a> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
|
||||
|
{% if mod|hasPermission(config.mod.createusers) %} |
||||
|
<p style="text-align:center"> |
||||
|
<a href="?/users/new">Create a new user</a> |
||||
|
</p> |
||||
|
{% endif %} |
||||
|
|
@ -0,0 +1,163 @@ |
|||||
|
{% for board_posts in posts %} |
||||
|
<fieldset> |
||||
|
<legend> |
||||
|
<a href="?/{{ config.board_path|sprintf(board_posts.board.uri) }}{{ config.file_index }}"> |
||||
|
{{ config.board_abbreviation|sprintf(board_posts.board.uri) }} |
||||
|
- |
||||
|
{{ board_posts.board.title|e }} |
||||
|
</a> |
||||
|
</legend> |
||||
|
{{ board_posts.posts|join('<hr>') }} |
||||
|
</fieldset> |
||||
|
{% endfor %} |
||||
|
|
||||
|
{% if mod|hasPermission(config.mod.view_notes) %} |
||||
|
<fieldset id="notes"> |
||||
|
<legend> |
||||
|
{% set notes_on_record = 'note' ~ (notes|count != 1 ? 's' : '') ~ ' on record' %} |
||||
|
<legend>{{ notes|count }} {% trans notes_on_record %}</legend> |
||||
|
</legend> |
||||
|
|
||||
|
{% if notes|count > 0 %} |
||||
|
<table class="modlog"> |
||||
|
<tr> |
||||
|
<th>{% trans 'Staff' %}</th> |
||||
|
<th>{% trans 'Note' %}</th> |
||||
|
<th>{% trans 'Date' %}</th> |
||||
|
{% if mod|hasPermission(config.mod.remove_notes) %} |
||||
|
<th>{% trans 'Actions' %}</th> |
||||
|
{% endif %} |
||||
|
</tr> |
||||
|
{% for note in notes %} |
||||
|
<tr> |
||||
|
<td class="minimal"> |
||||
|
{% if note.username %} |
||||
|
<a href="?/new_PM/{{ note.username|e }}">{{ note.username|e }}</a> |
||||
|
{% else %} |
||||
|
<em>{% trans 'deleted?' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td> |
||||
|
{{ note.body }} |
||||
|
</td> |
||||
|
<td class="minimal"> |
||||
|
{{ note.time|date(config.post_date) }} |
||||
|
</td> |
||||
|
{% if mod|hasPermission(config.mod.remove_notes) %} |
||||
|
<td class="minimal"> |
||||
|
<a href="?/IP/{{ ip }}/remove_note/{{ note.id }}"> |
||||
|
<small>[{% trans 'remove' %}]</small> |
||||
|
</a> |
||||
|
</td> |
||||
|
{% endif %} |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</table> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if mod|hasPermission(config.mod.create_notes) %} |
||||
|
<form action="" method="post" style="margin:0"> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<th>{% trans 'Staff' %}</th> |
||||
|
<td>{{ mod.username|e }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th> |
||||
|
<label for="note">{% trans 'Note' %}</label> |
||||
|
</th> |
||||
|
<td> |
||||
|
<textarea id="note" name="note" rows="5" cols="30"></textarea> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td></td> |
||||
|
<td><input type="submit" value="{% trans 'New note' %}"></td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</form> |
||||
|
{% endif %} |
||||
|
</fieldset> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if bans|count > 0 and mod|hasPermission(config.mod.view_ban) %} |
||||
|
<fieldset id="bans"> |
||||
|
{% set bans_on_record = 'ban' ~ (bans|count != 1 ? 's' : '') ~ ' on record' %} |
||||
|
<legend>{{ bans|count }} {% trans bans_on_record %}</legend> |
||||
|
|
||||
|
{% for ban in bans %} |
||||
|
<form action="" method="post" style="text-align:center"> |
||||
|
<table style="width:400px;margin-bottom:10px;border-bottom:1px solid #ddd;padding:5px"> |
||||
|
<tr> |
||||
|
<th>{% trans 'Status' %}</th> |
||||
|
<td> |
||||
|
{% if config.mod.view_banexpired and ban.expires != 0 and ban.expires < time() %} |
||||
|
{% trans 'Expired' %} |
||||
|
{% else %} |
||||
|
{% trans 'Active' %} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'IP' %}</th> |
||||
|
<td>{{ ban.ip }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Reason' %}</th> |
||||
|
<td> |
||||
|
{% if ban.reason %} |
||||
|
{{ ban.reason }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'no reason' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Board' %}</th> |
||||
|
<td> |
||||
|
{% if ban.board %} |
||||
|
{{ config.board_abbreviation|sprintf(ban.board) }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'all boards' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Set' %}</th> |
||||
|
<td>{{ ban.set|date(config.post_date) }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Expires' %}</th> |
||||
|
<td> |
||||
|
{% if ban.expires %} |
||||
|
{{ ban.expires|date(config.post_date) }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'never' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>{% trans 'Staff' %}</th> |
||||
|
<td> |
||||
|
{% if ban.username %} |
||||
|
{{ ban.username|e }} |
||||
|
{% else %} |
||||
|
<em>{% trans 'deleted?' %}</em> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
<input type="hidden" name="ban_id" value="{{ ban.id }}"> |
||||
|
<input type="submit" name="unban" value="{% trans 'Remove ban' %}"> |
||||
|
</form> |
||||
|
{% endfor %} |
||||
|
</fieldset> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if mod|hasPermission(config.mod.ban) %} |
||||
|
<fieldset> |
||||
|
<legend>{% trans 'New ban' %}</legend> |
||||
|
{% set redirect = '?/IP/' ~ ip ~ '#bans' %} |
||||
|
{% include 'mod/ban_form.html' %} |
||||
|
</fieldset> |
||||
|
{% endif %} |
Loading…
Reference in new issue