mirror of
https://github.com/vichan-devel/vichan.git
synced 2024-11-23 23:20:57 +01:00
begin implementation of in-built ban appealing
This commit is contained in:
parent
df143c6b50
commit
a9b7f9b1bc
@ -523,9 +523,31 @@
|
||||
// pure-PHP geolocation library.
|
||||
$config['country_flags'] = false;
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* Ban settings
|
||||
* ====================
|
||||
*/
|
||||
|
||||
// Require users to see the ban page at least once for a ban even if it has since expired.
|
||||
$config['require_ban_view'] = true;
|
||||
|
||||
// Show the post the user was banned for on the "You are banned" page.
|
||||
$config['ban_show_post'] = false;
|
||||
|
||||
// Optional HTML to append to "You are banned" pages. For example, you could include instructions and/or
|
||||
// a link to an email address or IRC chat room to appeal the ban.
|
||||
$config['ban_page_extra'] = '';
|
||||
|
||||
// Allow users to appeal bans through Tinyboard.
|
||||
$config['ban_appeals'] = false;
|
||||
|
||||
// Do not allow users to appeal bans that are shorter than this length (in seconds).
|
||||
$config['ban_appeals_min_length'] = 60 * 60 * 6; // 6 hours
|
||||
|
||||
// How many ban appeals can be made for a single ban?
|
||||
$config['ban_appeals_max'] = 1;
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* Markup settings
|
||||
@ -821,13 +843,6 @@
|
||||
// Automatically remove unnecessary whitespace when compiling HTML files from templates.
|
||||
$config['minify_html'] = true;
|
||||
|
||||
// Show the post the user was banned for on the "You are banned" page.
|
||||
$config['ban_show_post'] = false;
|
||||
|
||||
// Optional HTML to append to "You are banned" pages. For example, you could include instructions and/or
|
||||
// a link to an email address or IRC chat room to appeal the ban.
|
||||
$config['ban_page_extra'] = '';
|
||||
|
||||
// Display flags (when available). This config option has no effect unless poster flags are enabled (see
|
||||
// $config['country_flags']). Disable this if you want all previously-assigned flags to be hidden.
|
||||
$config['display_flags'] = true;
|
||||
@ -1322,6 +1337,10 @@
|
||||
$config['mod']['debug_sql'] = DISABLED;
|
||||
// Edit the current configuration (via web interface)
|
||||
$config['mod']['edit_config'] = ADMIN;
|
||||
// View ban appeals
|
||||
$config['mod']['view_ban_appeals'] = MOD;
|
||||
// Accept and deny ban appeals
|
||||
$config['mod']['ban_appeals'] = MOD;
|
||||
|
||||
// Config editor permissions
|
||||
$config['mod']['config'] = array();
|
||||
|
@ -625,11 +625,16 @@ function displayBan($ban) {
|
||||
|
||||
$ban['ip'] = $_SERVER['REMOTE_ADDR'];
|
||||
if ($ban['post'] && isset($ban['post']['board'], $ban['post']['id'])) {
|
||||
openBoard($ban['post']['board']);
|
||||
|
||||
$query = query(sprintf("SELECT `thumb`, `file` FROM ``posts_%s`` WHERE `id` = " . (int)$ban['post']['id'], $board['uri']));
|
||||
if ($_post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
$ban['post'] = array_merge($ban['post'], $_post);
|
||||
if (openBoard($ban['post']['board'])) {
|
||||
|
||||
$query = query(sprintf("SELECT `thumb`, `file` FROM ``posts_%s`` WHERE `id` = " .
|
||||
(int)$ban['post']['id'], $board['uri']));
|
||||
if ($_post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
$ban['post'] = array_merge($ban['post'], $_post);
|
||||
} else {
|
||||
$ban['post']['file'] = 'deleted';
|
||||
$ban['post']['thumb'] = false;
|
||||
}
|
||||
} else {
|
||||
$ban['post']['file'] = 'deleted';
|
||||
$ban['post']['thumb'] = false;
|
||||
@ -641,6 +646,21 @@ function displayBan($ban) {
|
||||
$post = new Thread($ban['post'], null, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
$denied_appeals = array();
|
||||
$pending_appeal = false;
|
||||
|
||||
if ($config['ban_appeals']) {
|
||||
$query = query("SELECT `time`, `denied` FROM `ban_appeals` WHERE `ban_id` = " . (int)$ban['id']) or error(db_error());
|
||||
while ($ban_appeal = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
if ($ban_appeal['denied']) {
|
||||
$denied_appeals[] = $ban_appeal['time'];
|
||||
} else {
|
||||
$pending_appeal = $ban_appeal['time'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show banned page and exit
|
||||
die(
|
||||
Element('page.html', array(
|
||||
@ -651,7 +671,9 @@ function displayBan($ban) {
|
||||
'config' => $config,
|
||||
'ban' => $ban,
|
||||
'board' => $board,
|
||||
'post' => isset($post) ? $post->build(true) : false
|
||||
'post' => isset($post) ? $post->build(true) : false,
|
||||
'denied_appeals' => $denied_appeals,
|
||||
'pending_appeal' => $pending_appeal
|
||||
)
|
||||
))
|
||||
));
|
||||
|
@ -197,37 +197,7 @@ function mod_search($type, $search_query_escaped, $page_no = 1) {
|
||||
// Form a series of LIKE clauses for the query.
|
||||
// This gets a little complicated.
|
||||
|
||||
// Escape "escape" character
|
||||
$query = str_replace('!', '!!', $query);
|
||||
|
||||
// Escape SQL wildcard
|
||||
$query = str_replace('%', '!%', $query);
|
||||
|
||||
// Use asterisk as wildcard instead
|
||||
$query = str_replace('*', '%', $query);
|
||||
|
||||
$query = str_replace('`', '!`', $query);
|
||||
|
||||
// Array of phrases to match
|
||||
$match = array();
|
||||
|
||||
// Exact phrases ("like this")
|
||||
if (preg_match_all('/"(.+?)"/', $query, $exact_phrases)) {
|
||||
$exact_phrases = $exact_phrases[1];
|
||||
foreach ($exact_phrases as $phrase) {
|
||||
$query = str_replace("\"{$phrase}\"", '', $query);
|
||||
$match[] = $pdo->quote($phrase);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-exact phrases (ie. plain keywords)
|
||||
$keywords = explode(' ', $query);
|
||||
foreach ($keywords as $word) {
|
||||
if (empty($word))
|
||||
continue;
|
||||
$match[] = $pdo->quote($word);
|
||||
}
|
||||
|
||||
|
||||
// Which `field` to search?
|
||||
if ($type == 'posts')
|
||||
$sql_field = array('body_nomarkup', 'filename', 'subject', 'filehash', 'ip', 'name', 'trip');
|
||||
@ -238,22 +208,6 @@ function mod_search($type, $search_query_escaped, $page_no = 1) {
|
||||
if ($type == 'log')
|
||||
$sql_field = 'text';
|
||||
|
||||
// Build the "LIKE 'this' AND LIKE 'that'" etc. part of the SQL query
|
||||
$sql_like = '';
|
||||
foreach ($match as $phrase) {
|
||||
if (!empty($sql_like))
|
||||
$sql_like .= ' AND ';
|
||||
$phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase);
|
||||
if (is_array($sql_field)) {
|
||||
foreach ($sql_field as $field) {
|
||||
$sql_like .= '`' . $field . '` LIKE ' . $phrase . ' ESCAPE \'!\' OR';
|
||||
}
|
||||
$sql_like = preg_replace('/ OR$/', '', $sql_like);
|
||||
} else {
|
||||
$sql_like .= '`' . $sql_field . '` LIKE ' . $phrase . ' ESCAPE \'!\'';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compile SQL query
|
||||
|
||||
@ -884,6 +838,68 @@ function mod_bans($page_no = 1) {
|
||||
mod_page(_('Ban list'), 'mod/ban_list.html', array('bans' => $bans, 'count' => Bans::count()));
|
||||
}
|
||||
|
||||
function mod_ban_appeals() {
|
||||
global $config, $board;
|
||||
|
||||
if (!hasPermission($config['mod']['view_ban_appeals']))
|
||||
error($config['error']['noaccess']);
|
||||
|
||||
// Remove stale ban appeals
|
||||
query("DELETE FROM ``ban_appeals`` WHERE NOT EXISTS (SELECT 1 FROM ``bans`` WHERE `ban_id` = ``bans``.`id`)")
|
||||
or error(db_error());
|
||||
|
||||
if (isset($_POST['appeal_id']) && (isset($_POST['unban']) || isset($_POST['deny']))) {
|
||||
if (!hasPermission($config['mod']['ban_appeals']))
|
||||
error($config['error']['noaccess']);
|
||||
if (isset($_POST['unban'])) {
|
||||
$query = query("SELECT `ban_id` FROM ``ban_appeals`` WHERE `id` = " .
|
||||
(int)$_POST['appeal_id']) or error(db_error());
|
||||
if ($ban_id = $query->fetchColumn()) {
|
||||
Bans::delete($ban_id);
|
||||
query("DELETE FROM ``ban_appeals`` WHERE `id` = " . (int)$_POST['appeal_id']) or error(db_error());
|
||||
}
|
||||
} else {
|
||||
query("UPDATE ``ban_appeals`` SET `denied` = 1 WHERE `id` = " . (int)$_POST['appeal_id']) or error(db_error());
|
||||
}
|
||||
|
||||
header('Location: ?/ban-appeals', true, $config['redirect_http']);
|
||||
return;
|
||||
}
|
||||
|
||||
$query = query("SELECT *, ``ban_appeals``.`id` AS `id` FROM ``ban_appeals``
|
||||
LEFT JOIN ``bans`` ON `ban_id` = ``bans``.`id`
|
||||
WHERE `denied` != 1 ORDER BY `time`") or error(db_error());
|
||||
$ban_appeals = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($ban_appeals as &$ban) {
|
||||
if ($ban['post'])
|
||||
$ban['post'] = json_decode($ban['post'], true);
|
||||
$ban['mask'] = Bans::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
|
||||
if ($ban['post'] && isset($ban['post']['board'], $ban['post']['id'])) {
|
||||
if (openBoard($ban['post']['board'])) {
|
||||
$query = query(sprintf("SELECT `thumb`, `file` FROM ``posts_%s`` WHERE `id` = " .
|
||||
(int)$ban['post']['id'], $board['uri']));
|
||||
if ($_post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
$ban['post'] = array_merge($ban['post'], $_post);
|
||||
} else {
|
||||
$ban['post']['file'] = 'deleted';
|
||||
$ban['post']['thumb'] = false;
|
||||
}
|
||||
} else {
|
||||
$ban['post']['file'] = 'deleted';
|
||||
$ban['post']['thumb'] = false;
|
||||
}
|
||||
|
||||
if ($ban['post']['thread']) {
|
||||
$ban['post'] = new Post($ban['post']);
|
||||
} else {
|
||||
$ban['post'] = new Thread($ban['post'], null, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod_page(_('Ban appeals'), 'mod/ban_appeals.html', array('ban_appeals' => $ban_appeals));
|
||||
}
|
||||
|
||||
function mod_lock($board, $unlock, $post) {
|
||||
global $config;
|
||||
|
1
mod.php
1
mod.php
@ -59,6 +59,7 @@ $pages = array(
|
||||
'/IP/([\w.:]+)/remove_note/(\d+)' => 'ip_remove_note', // remove note from ip address
|
||||
'/bans' => 'bans', // ban list
|
||||
'/bans/(\d+)' => 'bans', // ban list
|
||||
'/ban-appeals' => 'ban_appeals', // view ban appeals
|
||||
|
||||
'/search' => 'search_redirect', // search
|
||||
'/search/(posts|IP_notes|bans|log)/(.+)/(\d+)' => 'search', // search
|
||||
|
41
post.php
41
post.php
@ -763,6 +763,47 @@ if (isset($_POST['delete'])) {
|
||||
'id' => $id
|
||||
));
|
||||
}
|
||||
} elseif (isset($_POST['appeal'])) {
|
||||
if (!isset($_POST['ban_id']))
|
||||
error($config['error']['bot']);
|
||||
|
||||
$ban_id = (int)$_POST['ban_id'];
|
||||
|
||||
$bans = Bans::find($_SERVER['REMOTE_ADDR']);
|
||||
foreach ($bans as $_ban) {
|
||||
if ($_ban['id'] == $ban_id) {
|
||||
$ban = $_ban;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($ban)) {
|
||||
error(_("That ban doesn't exist or is not for you."));
|
||||
}
|
||||
|
||||
if ($ban['expires'] && $ban['expires'] - $ban['created'] <= $config['ban_appeals_min_length']) {
|
||||
error(_("You cannot appeal a ban of this length."));
|
||||
}
|
||||
|
||||
$query = query("SELECT `denied` FROM ``ban_appeals`` WHERE `ban_id` = $ban_id") or error(db_error());
|
||||
$ban_appeals = $query->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (count($ban_appeals) >= $config['ban_appeals_max']) {
|
||||
error(_("You cannot appeal this ban again."));
|
||||
}
|
||||
|
||||
foreach ($ban_appeals as $is_denied) {
|
||||
if (!$is_denied)
|
||||
error(_("There is already a pending appeal for this ban."));
|
||||
}
|
||||
|
||||
$query = prepare("INSERT INTO ``ban_appeals`` VALUES (NULL, :ban_id, :time, :message, 0)");
|
||||
$query->bindValue(':ban_id', $ban_id, PDO::PARAM_INT);
|
||||
$query->bindValue(':time', time(), PDO::PARAM_INT);
|
||||
$query->bindValue(':message', $_POST['appeal']);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
displayBan($ban);
|
||||
} else {
|
||||
if (!file_exists($config['has_installed'])) {
|
||||
header('Location: install.php', true, $config['redirect_http']);
|
||||
|
@ -424,4 +424,10 @@ table.mod.config-editor input[type="text"] {
|
||||
p.intro.thread-hidden {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
form.ban-appeal {
|
||||
margin: 9px 20px;
|
||||
}
|
||||
form.ban-appeal textarea {
|
||||
display: block;
|
||||
}
|
@ -77,16 +77,60 @@
|
||||
</p>
|
||||
<p>{% trans %}Your IP address is{% endtrans %} <strong>{{ ban.ip }}</strong>.</p>
|
||||
|
||||
{% if post %}
|
||||
{% if config.ban_page_extra %}
|
||||
<p>{{ config.ban_page_extra }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if post and config.ban_show_post %}
|
||||
<hr>
|
||||
<p>You were banned for the following post on {{ board.url }}:</p>
|
||||
<p>{% trans %}You were banned for the following post on {% endtrans %}{{ board.url }}:</p>
|
||||
{{ post }}
|
||||
<br>
|
||||
{% endif %}
|
||||
|
||||
{% if config.ban_page_extra %}
|
||||
<p>{{ config.ban_page_extra }}</p>
|
||||
{% if config.ban_appeals %}
|
||||
<hr>
|
||||
{% if pending_appeal %}
|
||||
<p>
|
||||
{% trans %}You submitted an appeal for this ban on{% endtrans %}
|
||||
<strong>{{ pending_appeal|date(config.ban_date) }}</strong>. {% trans %}It is still pending{% endtrans %}.
|
||||
</p>
|
||||
{% elseif denied_appeals|length >= config.ban_appeals_max %}
|
||||
{% if denied_appeals|length == 1 %}
|
||||
<p>
|
||||
{% trans %}You appealed this ban on{% endtrans %}
|
||||
<strong>{{ denied_appeals[0]|date(config.ban_date) }}</strong>
|
||||
{% trans %}and it was denied. You may not appeal this ban again.{% endtrans %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{% trans %}You have submitted the maximum number of ban appeals allowed. You may not appeal this ban again.{% endtrans %}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if denied_appeals|length %}
|
||||
{% if denied_appeals|length == 1 %}
|
||||
<p>
|
||||
{% trans %}You appealed this ban on{% endtrans %}
|
||||
<strong>{{ denied_appeals[0]|date(config.ban_date) }}</strong>
|
||||
{% trans %}and it was denied.{% endtrans %}
|
||||
</p>
|
||||
<p>{% trans %}You may appeal this ban again. Please enter your reasoning below.{% endtrans %}</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{% trans %}You last appealed this ban on{% endtrans %}
|
||||
<strong>{{ denied_appeals[denied_appeals|length - 1]|date(config.ban_date) }}</strong>
|
||||
{% trans %}and it was denied.{% endtrans %}
|
||||
</p>
|
||||
<p>{% trans %}You may appeal this ban again. Please enter your reasoning below.{% endtrans %}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p>{% trans %}You may appeal this ban. Please enter your reasoning below.{% endtrans %}</p>
|
||||
{% endif %}
|
||||
<form class="ban-appeal" action="" method="post">
|
||||
<input type="hidden" name="ban_id" value="{{ ban.id }}">
|
||||
<textarea name="appeal" rows="4" cols="40"></textarea>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfilter %}
|
||||
|
||||
{% endfilter %}
|
106
templates/mod/ban_appeals.html
Normal file
106
templates/mod/ban_appeals.html
Normal file
@ -0,0 +1,106 @@
|
||||
{% for ban in ban_appeals %}
|
||||
|
||||
<form action="" method="post" style="margin: 10px 0">
|
||||
<table style="margin: 5px 0">
|
||||
<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>
|
||||
{% if mod|hasPermission(config.mod.show_ip, board.uri) %}
|
||||
<tr>
|
||||
<th>{% trans 'IP' %}</th>
|
||||
<td>{{ ban.mask }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<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.created|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 'Seen' %}</th>
|
||||
<td>
|
||||
{% if ban.seen %}
|
||||
{% trans 'Yes' %}
|
||||
{% else %}
|
||||
{% trans 'No' %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'Staff' %}</th>
|
||||
<td>
|
||||
{% if ban.username %}
|
||||
{{ ban.username|e }}
|
||||
{% else %}
|
||||
<em>{% trans 'deleted?' %}</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table style="margin: 10px 0">
|
||||
<tr>
|
||||
<th>{% trans 'Appeal time' %}</th>
|
||||
<td>{{ ban.time|date(config.post_date) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'Appeal reason' %}</th>
|
||||
<td>{{ ban.message|e }}</td>
|
||||
</tr>
|
||||
{% if mod|hasPermission(config.mod.ban_appeals, board.uri) %}
|
||||
<tr>
|
||||
<th>{% trans 'Action' %}</th>
|
||||
<td>
|
||||
<input type="hidden" name="appeal_id" value="{{ ban.id }}">
|
||||
<input type="submit" name="unban" value="Unban">
|
||||
<input type="submit" name="deny" value="Deny appeal">
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
{% if ban.post %}
|
||||
<div style="">
|
||||
{{ ban.post.build(true) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
{% endfor %}
|
@ -86,6 +86,9 @@
|
||||
{% if mod|hasPermission(config.mod.view_banlist) %}
|
||||
<li><a href="?/bans">{% trans 'Ban list' %}</a></li>
|
||||
{% endif %}
|
||||
{% if config.ban_appeals and mod|hasPermission(config.mod.view_ban_appeals) %}
|
||||
<li><a href="?/ban-appeals">{% trans 'Ban appeals' %}</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) %}
|
||||
|
Loading…
Reference in New Issue
Block a user