[#184] Reports made to clean posts are now ignored correctly (and in accordance with the local/global duality). Marking posts clean dismisses posts (also in accordance with local/global duality). Fixed up some templating issues (now display more correct messages and omit actions that are invalid). Moved Clean action off post-level and into reports action.

This commit is contained in:
unknown 2014-10-21 07:50:25 -05:00
parent 4c8e920303
commit 9c5ec12aaa
7 changed files with 151 additions and 70 deletions

View File

@ -343,6 +343,7 @@ function embed_html($link) {
return 'Embedding error.'; return 'Embedding error.';
} }
class Post { class Post {
public function __construct($post, $root=null, $mod=false) { public function __construct($post, $root=null, $mod=false) {
global $config; global $config;
@ -410,7 +411,14 @@ class Post {
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
if( !($this->clean = $query->fetch(PDO::FETCH_ASSOC)) ) { if( !($this->clean = $query->fetch(PDO::FETCH_ASSOC)) ) {
$this->clean = array(); $this->clean = array(
'post_id' => $this->id,
'board_id' => $board['uri'],
'clean_local' => "0",
'clean_global' => "0",
'clean_local_mod_id' => null,
'clean_global_mod_id' => null,
);
} }
} }
@ -418,7 +426,7 @@ class Post {
} }
}; };
class Thread { class Thread extends Post {
public function __construct($post, $root = null, $mod = false, $hr = true) { public function __construct($post, $root = null, $mod = false, $hr = true) {
global $config; global $config;
if (!isset($root)) if (!isset($root))
@ -491,22 +499,5 @@ class Thread {
return $built; return $built;
} }
public function getClean( ) {
global $board, $config, $debug;
if( !isset( $this->clean ) ) {
$query = prepare("SELECT * FROM `post_clean` WHERE `post_id` = :post AND `board_id` = :board");
$query->bindValue( ':board', $board['uri'] );
$query->bindValue( ':post', $this->id );
$query->execute() or error(db_error($query));
if( !($this->clean = $query->fetch(PDO::FETCH_ASSOC)) ) {
$this->clean = array();
}
}
return $this->clean;
}
}; };

View File

@ -1614,7 +1614,7 @@ function mod_edit_post($board, $edit_raw_html, $postID) {
} }
buildIndex(); buildIndex();
rebuildThemes('post', $board); rebuildThemes('post', $board);
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . sprintf($config['file_page'], $post['thread'] ? $post['thread'] : $postID) . '#' . $postID, true, $config['redirect_http']); header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . sprintf($config['file_page'], $post['thread'] ? $post['thread'] : $postID) . '#' . $postID, true, $config['redirect_http']);
@ -2384,6 +2384,11 @@ function mod_reports() {
$po = new Post($content, '?/', $mod); $po = new Post($content, '?/', $mod);
} }
// Fetch clean status.
$po->getClean();
$clean = $po->clean;
// Add each report's template to this container. // Add each report's template to this container.
$report_html = ""; $report_html = "";
$reports_can_demote = false; $reports_can_demote = false;
@ -2396,6 +2401,7 @@ function mod_reports() {
'config' => $config, 'config' => $config,
'mod' => $mod, 'mod' => $mod,
'global' => $global, 'global' => $global,
'clean' => $clean,
'uri_dismiss' => "?/{$uri_report_base}/dismiss", 'uri_dismiss' => "?/{$uri_report_base}/dismiss",
'uri_ip' => "?/{$uri_report_base}/dismissall", 'uri_ip' => "?/{$uri_report_base}/dismissall",
@ -2430,8 +2436,14 @@ function mod_reports() {
$content_reports $content_reports
); );
$uri_content_base = "reports/" . ($global ? "global/" : "" ) . "content/";
$uri_clean_base = "{$report_item['board_id']}/clean/{$content['id']}"; // Figure out some stuff we need for the page.
$reports_can_demote = ( $clean['clean_local'] ? false : $reports_can_demote );
$reports_can_promote = ( $clean['clean_global'] ? false : $reports_can_promote );
$uri_content_base = "reports/" . ($global ? "global/" : "" ) . "content/";
$uri_clean_base = "reports/" . ($global ? "global/" : "" ) . "{$report_item['board_id']}/clean/{$content['id']}";
// Build the actions page.
$content_html = Element('mod/report_content.html', array( $content_html = Element('mod/report_content.html', array(
'reports_html' => $report_html, 'reports_html' => $report_html,
'reports_can_demote' => $reports_can_demote, 'reports_can_demote' => $reports_can_demote,
@ -2441,7 +2453,9 @@ function mod_reports() {
'content_html' => $po->build(true), 'content_html' => $po->build(true),
'content_board' => $report_item['board_id'], 'content_board' => $report_item['board_id'],
'content' => $content, 'content' => (array) $content,
'clean' => $clean,
'uri_content_demote' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/demote", 'uri_content_demote' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/demote",
'uri_content_promote' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/promote", 'uri_content_promote' => "?/{$uri_content_base}{$report_item['board_id']}/{$content['id']}/promote",
@ -2813,7 +2827,7 @@ function mod_recent_posts($lim) {
} }
function mod_clean( $board, $unclean, $post, $global, $local ) { function mod_report_clean( $global_reports, $board, $unclean, $post, $global, $local ) {
global $config, $mod; global $config, $mod;
if( !openBoard($board) ) { if( !openBoard($board) ) {
@ -2853,7 +2867,7 @@ function mod_clean( $board, $unclean, $post, $global, $local ) {
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
// If the $clean object doesn't exist we need to insert a row for this post. // If the $clean object doesn't exist we need to insert a row for this post.
if( !($clean = $query->fetch(PDO::FETCH_ASSOC)) ) { if( !($cleanRecord = $query->fetch(PDO::FETCH_ASSOC)) ) {
$query = prepare("INSERT INTO `post_clean` (`post_id`, `board_id`) VALUES ( :post, :board )"); $query = prepare("INSERT INTO `post_clean` (`post_id`, `board_id`) VALUES ( :post, :board )");
$query->bindValue( ':board', $board ); $query->bindValue( ':board', $board );
$query->bindValue( ':post', $post ); $query->bindValue( ':post', $post );
@ -2864,7 +2878,7 @@ function mod_clean( $board, $unclean, $post, $global, $local ) {
error("The database failed to create a record for this content in `post_clean` to record clean status."); error("The database failed to create a record for this content in `post_clean` to record clean status.");
} }
$clean = true; $cleanRecord = true;
} }
} }
// Revoking clean status (open it to reports?) // Revoking clean status (open it to reports?)
@ -2876,13 +2890,13 @@ function mod_clean( $board, $unclean, $post, $global, $local ) {
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
if( !($clean = $query->fetch(PDO::FETCH_ASSOC)) ) { if( !($cleanRecord = $query->fetch(PDO::FETCH_ASSOC)) ) {
error($config['error']['404']); error($config['error']['404']);
} }
} }
// Update the `post_clean` row represented by $clean. // Update the `post_clean` row represented by $clean.
if( $clean ) { if( $cleanRecord ) {
// Build our query based on the URI arguments. // Build our query based on the URI arguments.
if( $global && $local ) { if( $global && $local ) {
$query = prepare("UPDATE `post_clean` SET {$query_global}, {$query_global_mod}, {$query_local}, {$query_local_mod} WHERE `board_id` = :board AND `post_id` = :post"); $query = prepare("UPDATE `post_clean` SET {$query_global}, {$query_global_mod}, {$query_local}, {$query_local_mod} WHERE `board_id` = :board AND `post_id` = :post");
@ -2895,21 +2909,64 @@ function mod_clean( $board, $unclean, $post, $global, $local ) {
} }
$query->bindValue( ':clean', !$unclean ); $query->bindValue( ':clean', !$unclean );
$query->bindValue( ':mod', $mod['id'] ); $query->bindValue( ':mod', $unclean ? NULL : $mod['id'] );
$query->bindValue( ':board', $board ); $query->bindValue( ':board', $board );
$query->bindValue( ':post', $post ); $query->bindValue( ':post', $post );
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
// Finally, run a query to tidy up our records.
if( $unclean ) {
// Query is removing clean status from content.
// Remove any clean records that are now null.
$cleanup = prepare("DELETE FROM `post_clean` WHERE `clean_local` = FALSE AND `clean_global` = FALSE");
$query->execute() or error(db_error($query));
}
else {
// Content is clean, auto-handle all reports.
// If this is a total clean, we don't need to update records first.
if( !($global && $local) ) {
$query = prepare("UPDATE `reports` SET `" . ($local ? "local" : "global") . "` = FALSE WHERE `board` = :board AND `post` = :post");
$query->bindValue( ':board', $board );
$query->bindValue( ':post', $post );
$query->execute() or error(db_error($query));
// If we didn't hit anything, this content doesn't have reports, so don't run the delete query.
$require_delete = ($query->rowCount() > 0);
if( $require_delete ) {
$query = prepare("DELETE FROM `reports` WHERE `local` = FALSE and `global` = FALSE");
$query->execute() or error(db_error($query));
}
}
// This is a total clean, so delete content by ID rather than via cleanup.
else {
$query = prepare("DELETE FROM `reports` WHERE `board` = :board AND `post` = :post");
$query->bindValue( ':board', $board );
$query->bindValue( ':post', $post );
$query->execute() or error(db_error($query));
}
}
// Log the action. // Log the action.
// This is super important because a mod intentionally screwing with clean status needs to be found out, fast. // Having clear wording of ths log is very important because of the nature of clean status.
$log_action = ($unclean ? "Closed" : "Re-opened" ); $log_action = ($unclean ? "Closed" : "Re-opened" );
$log_scope = ($local && $global ? "local and global" : ($local ? "local" : "global" ) ); $log_scope = ($local && $global ? "local and global" : ($local ? "local" : "global" ) );
modLog( "{$log_action} reports for post #{$post} in {$log_scope}.", $board); modLog( "{$log_action} reports for post #{$post} in {$log_scope}.", $board);
} }
// Redirect // Redirect
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']); if( $global_reports ) {
header('Location: ?/reports/global', true, $config['redirect_http']);
}
else {
header('Location: ?/reports', true, $config['redirect_http']);
}
} }
function mod_config($board_config = false) { function mod_config($board_config = false) {

20
mod.php
View File

@ -60,15 +60,16 @@ $pages = array(
// and if the return address should also be the global dashboard. // and if the return address should also be the global dashboard.
// Important to note that (?:global) will make no argument. // Important to note that (?:global) will make no argument.
// (global)? will make argument 0 either "global" or "". // (global)? will make argument 0 either "global" or "".
'/reports(?:/)?' => 'reports', // report queue '/reports(?:/)?' => 'reports', // report queue
'/reports/(global)?(?:/)?' => 'reports', // global report queue '/reports/(global)?(?:/)?' => 'reports', // global report queue
'/reports/(global)?(?:/)?(content)/(\%b)/(\d+)(?:/)?' => 'reports', // specific reported content (also historic) '/reports/(global)?(?:/)?(content)/(\%b)/(\d+)(?:/)?' => 'reports', // specific reported content (also historic)
'/reports/(global)?(?:/)?(content)/(\%b)/(\d+)/dismiss(?:/)?' => 'secure report_dismiss', // dismiss all reports on content '/reports/(global)?(?:/)?(content)/(\%b)/(\d+)/dismiss(?:/)?' => 'secure report_dismiss', // dismiss all reports on content
'/reports/(global)?(?:/)?(content)/(\%b)/(\d+)/demote(?:/)?' => 'secure report_demote', // demote all reports on content '/reports/(global)?(?:/)?(content)/(\%b)/(\d+)/demote(?:/)?' => 'secure report_demote', // demote all reports on content
'/reports/(global)?(?:/)?(content)/(\%b)/(\d+)/promote(?:/)?' => 'secure report_promote', // demote all reports on content '/reports/(global)?(?:/)?(content)/(\%b)/(\d+)/promote(?:/)?' => 'secure report_promote', // demote all reports on content
'/reports/(global)?(?:/)?(\d+)/dismiss(all)?(?:/)?' => 'secure report_dismiss', // dismiss a report '/reports/(global)?(?:/)?(\d+)/dismiss(all)?(?:/)?' => 'secure report_dismiss', // dismiss a report
'/reports/(global)?(?:/)?(\d+)/demote(?:/)?' => 'secure report_demote', // demote a global report to a local report '/reports/(global)?(?:/)?(\d+)/demote(?:/)?' => 'secure report_demote', // demote a global report to a local report
'/reports/(global)?(?:/)?(\d+)/promote(?:/)?' => 'secure report_promote', // promote a local report to a global report '/reports/(global)?(?:/)?(\d+)/promote(?:/)?' => 'secure report_promote', // promote a local report to a global report
'/reports/(global)?(?:/)?(\%b)/(un)?clean/(\d+)/(global)?(?:\+)?(local)?' => 'secure report_clean', // protect/unprotect from reports
'/IP/([\w.:]+)' => 'secure_POST ip', // view ip address '/IP/([\w.:]+)' => 'secure_POST ip', // view ip address
'/IP/([\w.:]+)/remove_note/(\d+)' => 'secure ip_remove_note', // remove note from ip address '/IP/([\w.:]+)/remove_note/(\d+)' => 'secure ip_remove_note', // remove note from ip address
@ -98,7 +99,6 @@ $pages = array(
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread '/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread '/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread '/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread
'/(\%b)/(un)?clean/(\d+)/(global)?(?:\+)?(local)?' => 'secure clean', // protect/unprotect from reports
'/themes' => 'themes_list', // manage themes '/themes' => 'themes_list', // manage themes
'/themes/(\w+)' => 'secure_POST theme_configure', // configure/reconfigure theme '/themes/(\w+)' => 'secure_POST theme_configure', // configure/reconfigure theme

View File

@ -104,7 +104,8 @@ if (isset($_POST['delete'])) {
header('Content-Type: text/json'); header('Content-Type: text/json');
echo json_encode(array('success' => true)); echo json_encode(array('success' => true));
} }
} elseif (isset($_POST['report'])) { }
elseif (isset($_POST['report'])) {
if (!isset($_POST['board'], $_POST['reason'])) if (!isset($_POST['board'], $_POST['reason']))
error($config['error']['bot']); error($config['error']['bot']);
@ -134,26 +135,45 @@ if (isset($_POST['delete'])) {
markup($reason); markup($reason);
foreach ($report as &$id) { foreach ($report as &$id) {
$query = prepare(sprintf("SELECT `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); $query = prepare(
"SELECT
`thread`,
`post_clean`.`clean_local`,
`post_clean`.`clean_global`
FROM `posts_{$board['uri']}`
LEFT JOIN `post_clean`
ON `post_clean`.`board_id` = '{$board['uri']}'
AND `post_clean`.`post_id` = :id
WHERE `id` = :id"
);
$query->bindValue(':id', $id, PDO::PARAM_INT); $query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
$thread = $query->fetchColumn(); if( $post = $query->fetch(PDO::FETCH_ASSOC) ) {
$report_local = !$post['clean_local'];
if ($config['syslog']) $report_global = isset($_POST['global']) && !$post['clean_global'];
_syslog(LOG_INFO, 'Reported post: ' .
'/' . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $thread ? $thread : $id) . ($thread ? '#' . $id : '') . if( $report_local || $report_global ) {
' for "' . $reason . '"' $thread = $post['thread'];
);
$query = prepare("INSERT INTO ``reports`` (`time`, `ip`, `board`, `post`, `reason`, `local`, `global`) VALUES (:time, :ip, :board, :post, :reason, :local, :global)"); if ($config['syslog']) {
$query->bindValue(':time', time(), PDO::PARAM_INT); _syslog(LOG_INFO, 'Reported post: ' .
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR); '/' . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $thread ? $thread : $id) . ($thread ? '#' . $id : '') .
$query->bindValue(':board', $board['uri'], PDO::PARAM_INT); ' for "' . $reason . '"'
$query->bindValue(':post', $id, PDO::PARAM_INT); );
$query->bindValue(':reason', $reason, PDO::PARAM_STR); }
$query->bindValue(':local', 1, PDO::PARAM_BOOL);
$query->bindValue(':global', isset($_POST['global']), PDO::PARAM_BOOL); $query = prepare("INSERT INTO `reports` (`time`, `ip`, `board`, `post`, `reason`, `local`, `global`) VALUES (:time, :ip, :board, :post, :reason, :local, :global)");
$query->execute() or error(db_error($query)); $query->bindValue(':time', time(), PDO::PARAM_INT);
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR);
$query->bindValue(':board', $board['uri'], PDO::PARAM_INT);
$query->bindValue(':post', $id, PDO::PARAM_INT);
$query->bindValue(':reason', $reason, PDO::PARAM_STR);
$query->bindValue(':local', $report_local, PDO::PARAM_BOOL);
$query->bindValue(':global', $report_global, PDO::PARAM_BOOL);
$query->execute() or error(db_error($query));
}
}
} }
$is_mod = isset($_POST['mod']) && $_POST['mod']; $is_mod = isset($_POST['mod']) && $_POST['mod'];
@ -165,7 +185,8 @@ if (isset($_POST['delete'])) {
header('Content-Type: text/json'); header('Content-Type: text/json');
echo json_encode(array('success' => true)); echo json_encode(array('success' => true));
} }
} elseif (isset($_POST['post'])) { }
elseif (isset($_POST['post'])) {
if (!isset($_POST['body'], $_POST['board'])) if (!isset($_POST['body'], $_POST['board']))
error($config['error']['bot']); error($config['error']['bot']);
@ -890,7 +911,8 @@ if (isset($_POST['delete'])) {
'id' => $id 'id' => $id
)); ));
} }
} elseif (isset($_POST['appeal'])) { }
elseif (isset($_POST['appeal'])) {
if (!isset($_POST['ban_id'])) if (!isset($_POST['ban_id']))
error($config['error']['bot']); error($config['error']['bot']);
@ -931,7 +953,8 @@ if (isset($_POST['delete'])) {
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
displayBan($ban); displayBan($ban);
} else { }
else {
if (!file_exists($config['has_installed'])) { if (!file_exists($config['has_installed'])) {
header('Location: install.php', true, $config['redirect_http']); header('Location: install.php', true, $config['redirect_http']);
} else { } else {

View File

@ -289,15 +289,15 @@ div.post.reply {
} }
div.post_modified { div.post_modified {
font-size: 10px;
min-width: 47.5em; min-width: 47.5em;
margin-left: 1.8em; margin-left: 1.8em;
padding-top: 0.8em; padding-top: 0.8em;
} }
div.post_modified div.content-status { div.post_modified div.content-status {
margin-top: 0.4em; margin-top: 0.5em;
padding-bottom: 0em; padding-bottom: 0em;
font-size: 72%;
} }
span.trip { span.trip {

View File

@ -32,7 +32,9 @@
{% if global and mod|hasPermission(config.mod.report_demote, report.board) %} {% if global and mod|hasPermission(config.mod.report_demote, report.board) %}
<li class="report-action"> <li class="report-action">
{% if report.local %} {% if clean.clean_local %}
<span class="content-action-item content-action-unavailable" title="{% trans 'Content is permitted by board rules' %}">Demote</span>
{% elseif report.local %}
<a class="action-item action-available" title="{% trans 'Demote global abuse report to a local report' %}" href="{{uri_demote}}/{{token_demote}}">Demote</a> <a class="action-item action-available" title="{% trans 'Demote global abuse report to a local report' %}" href="{{uri_demote}}/{{token_demote}}">Demote</a>
{% else %} {% else %}
<span class="action-item action-unavailable" title="{% trans 'Report has already been dismissed locally' %}">Demote</span> <span class="action-item action-unavailable" title="{% trans 'Report has already been dismissed locally' %}">Demote</span>
@ -40,7 +42,9 @@
</li> </li>
{% elseif not global and mod|hasPermission(config.mod.report_promote, report.board) %} {% elseif not global and mod|hasPermission(config.mod.report_promote, report.board) %}
<li class="report-action"> <li class="report-action">
{% if report.global %} {% if clean.clean_global %}
<span class="content-action-item content-action-unavailable" title="{% trans 'Content is permitted by global rules' %}">Promote</span>
{% elseif report.global %}
<span class="action-item action-unavailable" title="{% trans 'Report is already a global report' %}">Promote</span> <span class="action-item action-unavailable" title="{% trans 'Report is already a global report' %}">Promote</span>
{% else %} {% else %}
<a class="action-item action-available" title="{% trans 'Promote local abuse report to a global report' %}" href="{{uri_promote}}/{{token_promote}}">Promote</a> <a class="action-item action-available" title="{% trans 'Promote local abuse report to a global report' %}" href="{{uri_promote}}/{{token_promote}}">Promote</a>

View File

@ -18,6 +18,8 @@
<li class="report-action"> <li class="report-action">
{% if reports_can_demote %} {% if reports_can_demote %}
<a class="content-action-item content-action-available"title="{% trans 'Demote global abuse reports to local reports' %}" href="{{uri_content_demote}}/{{token_content_demote}}">Demote All</a> <a class="content-action-item content-action-available"title="{% trans 'Demote global abuse reports to local reports' %}" href="{{uri_content_demote}}/{{token_content_demote}}">Demote All</a>
{% elseif clean.clean_local %}
<span class="content-action-item content-action-unavailable" title="{% trans 'Content is permitted by board rules' %}">Demote All</span>
{% else %} {% else %}
<span class="content-action-item content-action-unavailable" title="{% trans 'Reports have all been dismissed locally' %}">Demote All</span> <span class="content-action-item content-action-unavailable" title="{% trans 'Reports have all been dismissed locally' %}">Demote All</span>
{% endif %} {% endif %}
@ -27,31 +29,35 @@
<li class="report-action"> <li class="report-action">
{% if reports_can_promote %} {% if reports_can_promote %}
<a class="content-action-item content-action-available"title="{% trans 'Promote all local abuse reports to global reports' %}" href="{{uri_content_promote}}/{{token_content_promote}}">Promote All</a> <a class="content-action-item content-action-available"title="{% trans 'Promote all local abuse reports to global reports' %}" href="{{uri_content_promote}}/{{token_content_promote}}">Promote All</a>
{% elseif clean.clean_global %}
<span class="content-action-item content-action-unavailable" title="{% trans 'Content is permitted by global rules' %}">Promote All</span>
{% else %} {% else %}
<span class="content-action-item content-action-unavailable" title="{% trans 'Reports are already global reports' %}">Promote All</span> <span class="content-action-item content-action-unavailable" title="{% trans 'Reports are already global reports' %}">Promote All</span>
{% endif %} {% endif %}
</li> </li>
{% endif %} {% endif %}
{% if not clean.clean_local or not clean.clean_global %}
{% if mod|hasPermission(config.mod.clean, report.board) or mod|hasPermission(config.mod.clean_global, report.board) %} {% if mod|hasPermission(config.mod.clean, report.board) or mod|hasPermission(config.mod.clean_global, report.board) %}
<li class="report-content-action"> <li class="report-content-action">
Clean&nbsp; Clean&nbsp;
{% if mod|hasPermission(config.mod.clean, report.board) %} {% if not clean.clean_local and mod|hasPermission(config.mod.clean, report.board) %}
<!-- Clean Local --> <!-- Clean Local -->
<a class="content-action-item content-action-available" title="{% trans 'Ignore and dismiss local abuse reports on this post for this board' %}" href="{{ uri_clean }}/{{ token_clean }}">(/{{ content_board }}/)</a> <a class="content-action-item content-action-available" title="{% trans 'Ignore and dismiss local abuse reports on this post for this board' %}" href="{{ uri_clean }}/{{ token_clean }}">(/{{ content_board }}/)</a>
{% endif %}
{% if mod|hasPermission(config.mod.clean_global, report.board) %}
&nbsp; &nbsp;
{% endif %}
{% if not clean.clean_global and mod|hasPermission(config.mod.clean_global, report.board) %}
<!-- Clean Global --> <!-- Clean Global -->
<a class="content-action-item content-action-available" title="{% trans 'Ignore and demote global abuse reports on this post' %}" href="{{ uri_clean_global }}/{{ token_clean_global }}">(Global)</a> <a class="content-action-item content-action-available" title="{% trans 'Ignore and demote global abuse reports on this post' %}" href="{{ uri_clean_global }}/{{ token_clean_global }}">(Global)</a>
{% if not clean.clean_local and mod|hasPermission(config.mod.clean, report.board) %}
&nbsp; &nbsp;
{% if mod|hasPermission(config.mod.clean, report.board) and mod|hasPermission(config.mod.clean_global, report.board) %}
<!-- Clean Local + Global --> <!-- Clean Local + Global -->
<a class="content-action-item content-action-available" title="{% trans 'Ignore and dismiss local AND global abuse reports on this post' %}" href="{{ uri_clean_both }}/{{ token_clean_both }}">(/{{ content_board }}/+Global)</a> <a class="content-action-item content-action-available" title="{% trans 'Ignore and dismiss local AND global abuse reports on this post' %}" href="{{ uri_clean_both }}/{{ token_clean_both }}">(/{{ content_board }}/+Global)</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
</li> </li>
{% endif %} {% endif %}
{% endif %}
</ul> </ul>
</div> </div>