[#184] Resolved duplicate report issue. Added rudamentary Clean functionality.

This commit is contained in:
unknown 2014-10-20 10:17:19 -05:00
parent 7cac272396
commit 4c8e920303
11 changed files with 241 additions and 53 deletions

View File

@ -1382,7 +1382,10 @@
$config['mod']['flood'] = &$config['mod']['bypass_filters']; $config['mod']['flood'] = &$config['mod']['bypass_filters'];
// Raw HTML posting // Raw HTML posting
$config['mod']['rawhtml'] = ADMIN; $config['mod']['rawhtml'] = ADMIN;
// Clean/Unclean posts
$config['mod']['clean'] = JANITOR;
$config['mod']['clean_global'] = MOD;
/* Administration */ /* Administration */
// View the report queue // View the report queue
$config['mod']['reports'] = JANITOR; $config['mod']['reports'] = JANITOR;

View File

@ -389,7 +389,32 @@ class Post {
public function build($index=false) { public function build($index=false) {
global $board, $config; global $board, $config;
return Element('post_reply.html', array('config' => $config, 'board' => $board, 'post' => &$this, 'index' => $index, 'mod' => $this->mod)); return Element('post_reply.html', array(
'config' => $config,
'board' => $board,
'post' => &$this,
'index' => $index,
'mod' => $this->mod,
'clean' => $this->getClean(),
));
}
public function getClean( ) {
global $board;
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;
} }
}; };
@ -453,9 +478,35 @@ class Thread {
event('show-thread', $this); event('show-thread', $this);
$built = Element('post_thread.html', array('config' => $config, 'board' => $board, 'post' => &$this, 'index' => $index, 'hasnoko50' => $hasnoko50, 'isnoko50' => $isnoko50, 'mod' => $this->mod)); $built = Element('post_thread.html', array(
'config' => $config,
'board' => $board,
'post' => &$this,
'index' => $index,
'hasnoko50' => $hasnoko50,
'isnoko50' => $isnoko50,
'mod' => $this->mod,
'clean' => $this->getClean(),
));
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

@ -311,6 +311,7 @@ function _syslog($priority, $message) {
function verbose_error_handler($errno, $errstr, $errfile, $errline) { function verbose_error_handler($errno, $errstr, $errfile, $errline) {
if (error_reporting() == 0) if (error_reporting() == 0)
return false; // Looks like this warning was suppressed by the @ operator. return false; // Looks like this warning was suppressed by the @ operator.
error(utf8tohtml($errstr), true, array( error(utf8tohtml($errstr), true, array(
'file' => $errfile . ':' . $errline, 'file' => $errfile . ':' . $errline,
'errno' => $errno, 'errno' => $errno,

View File

@ -2257,6 +2257,7 @@ function mod_rebuild() {
)); ));
} }
function mod_reports() { function mod_reports() {
global $config, $mod; global $config, $mod;
@ -2276,7 +2277,7 @@ function mod_reports() {
$report_scope = $global ? "global" : "local"; $report_scope = $global ? "global" : "local";
// Get REPORTS. // Get REPORTS.
$query = prepare("SELECT * FROM ``reports`` " . ($mod["type"] == "20" ? "WHERE board = :board" : "") . " WHERE ``".($global ? "global" : "local")."``=TRUE LIMIT :limit"); $query = prepare("SELECT * FROM ``reports`` " . ($mod["type"] == "20" ? "WHERE board = :board" : "") . " WHERE ``".($global ? "global" : "local")."`` = TRUE LIMIT :limit");
// Limit reports by board if the moderator is local. // Limit reports by board if the moderator is local.
if( $mod['type'] == '20' ) { if( $mod['type'] == '20' ) {
@ -2335,27 +2336,26 @@ function mod_reports() {
$report_index[ $content_key ] = array( $report_index[ $content_key ] = array(
"board_id" => $report['board'], "board_id" => $report['board'],
"post_id" => $report['post'], "post_id" => $report['post'],
"content" => &$report_posts[ $report['board'] ][ $report['post'] ], "content" => $report_posts[ $report['board'] ][ $report['post'] ],
"reports" => array(), "reports" => array(),
); );
} }
// Add the report to the list of reports. // Add the report to the list of reports.
$report_index[ $content_key ]['reports'][ $report['id'] ] = &$report; $report_index[ $content_key ]['reports'][ $report['id'] ] = $report;
// Increment the total report count. // Increment the total report count.
++$reportCount; ++$reportCount;
} }
// Only continue if we have something to do. // Only continue if we have something to do.
// If there are no valid reports left, we're done. // If there are no valid reports left, we're done.
if( $reportCount > 0 ) { if( $reportCount > 0 ) {
// Sort this report index by number of reports, desc. // Sort this report index by number of reports, desc.
usort( $report_index, function( $a, $b ) { usort( $report_index, function( $a, $b ) {
$ra = $a['reports']; $ra = count( $a['reports'] );
$rb = $b['reports']; $rb = count( $b['reports'] );
if( $ra < $rb ) { if( $ra < $rb ) {
return 1; return 1;
@ -2404,7 +2404,7 @@ function mod_reports() {
'token_dismiss' => make_secure_link_token( $uri_report_base . '/dismiss' ), 'token_dismiss' => make_secure_link_token( $uri_report_base . '/dismiss' ),
'token_ip' => make_secure_link_token( $uri_report_base . '/dismissall' ), 'token_ip' => make_secure_link_token( $uri_report_base . '/dismissall' ),
'token_demote' => make_secure_link_token( $uri_report_base . '/demote' ), 'token_demote' => make_secure_link_token( $uri_report_base . '/demote' ),
'token_promote' => make_secure_link_token( $uri_report_base . '/promote' ), 'token_promote' => make_secure_link_token( $uri_report_base . '/promote' ),
)); ));
// Determines if we can "Demote All" / "Promote All" // Determines if we can "Demote All" / "Promote All"
@ -2431,6 +2431,7 @@ function mod_reports() {
); );
$uri_content_base = "reports/" . ($global ? "global/" : "" ) . "content/"; $uri_content_base = "reports/" . ($global ? "global/" : "" ) . "content/";
$uri_clean_base = "{$report_item['board_id']}/clean/{$content['id']}";
$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,
@ -2449,6 +2450,13 @@ function mod_reports() {
'token_content_promote' => make_secure_link_token( "{$uri_content_base}{$report_item['board_id']}/{$content['id']}/promote" ), 'token_content_promote' => make_secure_link_token( "{$uri_content_base}{$report_item['board_id']}/{$content['id']}/promote" ),
'token_content_dismiss' => make_secure_link_token( "{$uri_content_base}{$report_item['board_id']}/{$content['id']}/dismiss" ), 'token_content_dismiss' => make_secure_link_token( "{$uri_content_base}{$report_item['board_id']}/{$content['id']}/dismiss" ),
'uri_clean' => "?/{$uri_clean_base}/local",
'uri_clean_global' => "?/{$uri_clean_base}/global",
'uri_clean_both' => "?/{$uri_clean_base}/global+local",
'token_clean' => make_secure_link_token( $uri_clean_base . '/local' ),
'token_clean_global' => make_secure_link_token( $uri_clean_base . '/global' ),
'token_clean_both' => make_secure_link_token( $uri_clean_base . '/global+local' ),
'global' => $global, 'global' => $global,
'config' => $config, 'config' => $config,
'mod' => $mod, 'mod' => $mod,
@ -2565,6 +2573,12 @@ function mod_report_dismiss() {
$query->execute() or error(db_error($query)); $query->execute() or error(db_error($query));
// Cleanup - Remove reports that have been completely dismissed.
$query = prepare("DELETE FROM `reports` WHERE `local` = FALSE AND `global` = FALSE");
$query->execute() or error(db_error($query));
if( $all ) { if( $all ) {
modLog("Dismissed all reports by <a href=\"?/IP/{$ip}\">{$ip}</a>"); modLog("Dismissed all reports by <a href=\"?/IP/{$ip}\">{$ip}</a>");
} }
@ -2799,6 +2813,105 @@ function mod_recent_posts($lim) {
} }
function mod_clean( $board, $unclean, $post, $global, $local ) {
global $config, $mod;
if( !openBoard($board) ) {
error($config['error']['noboard']);
}
$query_global = "";
$query_global_mod = "";
if( $global ) {
if( !hasPermission($config['mod']['clean_global'], $board) ) {
error($config['error']['noaccess']);
}
$query_global = "`clean_global` = :clean";
$query_global_mod = "`clean_global_mod_id` = :mod";
}
$query_local = "";
$query_local_mod = "";
if( $local ) {
if( !hasPermission($config['mod']['clean'], $board) ) {
error($config['error']['noaccess']);
}
$query_local = "`clean_local` = :clean";
$query_local_mod = "`clean_local_mod_id` = :mod";
}
// Marking this post as "Clean" (report immune?)
if( !$unclean ) {
// Attempt to find a `post_clean` row for this content.
$query = prepare("SELECT * FROM `post_clean` WHERE `board_id` = :board AND `post_id` = :post");
$query->bindValue( ':board', $board );
$query->bindValue( ':post', $post );
$query->execute() or error(db_error($query));
// If the $clean object doesn't exist we need to insert a row for this post.
if( !($clean = $query->fetch(PDO::FETCH_ASSOC)) ) {
$query = prepare("INSERT INTO `post_clean` (`post_id`, `board_id`) VALUES ( :post, :board )");
$query->bindValue( ':board', $board );
$query->bindValue( ':post', $post );
$query->execute() or error(db_error($query));
if( $query->rowCount() == 0 ) {
error("The database failed to create a record for this content in `post_clean` to record clean status.");
}
$clean = true;
}
}
// Revoking clean status (open it to reports?)
else {
// Attempt to find a `post_clean` row for this content.
$query = prepare("SELECT * FROM `post_clean` WHERE `board_id` = :board AND `post_id` = :post");
$query->bindValue( ':board', $board );
$query->bindValue( ':post', $post );
$query->execute() or error(db_error($query));
if( !($clean = $query->fetch(PDO::FETCH_ASSOC)) ) {
error($config['error']['404']);
}
}
// Update the `post_clean` row represented by $clean.
if( $clean ) {
// Build our query based on the URI arguments.
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");
}
else if( $global ) {
$query = prepare("UPDATE `post_clean` SET {$query_global}, {$query_global_mod} WHERE `board_id` = :board AND `post_id` = :post");
}
else {
$query = prepare("UPDATE `post_clean` SET {$query_local}, {$query_local_mod} WHERE `board_id` = :board AND `post_id` = :post");
}
$query->bindValue( ':clean', !$unclean );
$query->bindValue( ':mod', $mod['id'] );
$query->bindValue( ':board', $board );
$query->bindValue( ':post', $post );
$query->execute() or error(db_error($query));
// Log the action.
// This is super important because a mod intentionally screwing with clean status needs to be found out, fast.
$log_action = ($unclean ? "Closed" : "Re-opened" );
$log_scope = ($local && $global ? "local and global" : ($local ? "local" : "global" ) );
modLog( "{$log_action} reports for post #{$post} in {$log_scope}.", $board);
}
// Redirect
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
}
function mod_config($board_config = false) { function mod_config($board_config = false) {
global $config, $mod, $board; global $config, $mod, $board;

26
mod.php
View File

@ -85,18 +85,20 @@ $pages = array(
'/search' => 'search_redirect', // search '/search' => 'search_redirect', // search
'/search/(posts|IP_notes|bans|log)/(.+)/(\d+)' => 'search', // search '/search/(posts|IP_notes|bans|log)/(.+)/(\d+)' => 'search', // search
'/search/(posts|IP_notes|bans|log)/(.+)' => 'search', // search '/search/(posts|IP_notes|bans|log)/(.+)' => 'search', // search
'/(\%b)/ban(&delete)?/(\d+)' => 'secure_POST ban_post', // ban poster // Content management
'/(\%b)/move/(\d+)' => 'secure_POST move', // move thread '/(\%b)/ban(&delete)?/(\d+)' => 'secure_POST ban_post', // ban poster
'/(\%b)/move_reply/(\d+)' => 'secure_POST move_reply', // move reply '/(\%b)/move/(\d+)' => 'secure_POST move', // move thread
'/(\%b)/edit(_raw)?/(\d+)' => 'secure_POST edit_post', // edit post '/(\%b)/move_reply/(\d+)' => 'secure_POST move_reply', // move reply
'/(\%b)/delete/(\d+)' => 'secure delete', // delete post '/(\%b)/edit(_raw)?/(\d+)' => 'secure_POST edit_post', // edit post
'/(\%b)/deletefile/(\d+)/(\d+)' => 'secure deletefile', // delete file from post '/(\%b)/delete/(\d+)' => 'secure delete', // delete post
'/(\%b+)/spoiler/(\d+)/(\d+)' => 'secure spoiler_image', // spoiler file '/(\%b)/deletefile/(\d+)/(\d+)' => 'secure deletefile', // delete file from post
'/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address '/(\%b+)/spoiler/(\d+)/(\d+)' => 'secure spoiler_image', // spoiler file
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread '/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread '/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread '/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky 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

@ -288,6 +288,18 @@ div.post.reply {
max-width: 94%!important; max-width: 94%!important;
} }
div.post_modified {
font-size: 10px;
min-width: 47.5em;
margin-left: 1.8em;
padding-top: 0.8em;
}
div.post_modified div.content-status {
margin-top: 0.4em;
padding-bottom: 0em;
}
span.trip { span.trip {
color: #228854; color: #228854;
} }

View File

@ -1,5 +1,5 @@
<li class="report-item"> <li class="report-item">
<div class="report"> <div class="report" id="report-{{ report.id }}">
<span class="report-reason">{% if report.reason %}{{ report.reason }}{% else %}<em>{% trans 'No reason given.' %}</em>{% endif %}</span> <span class="report-reason">{% if report.reason %}{{ report.reason }}{% else %}<em>{% trans 'No reason given.' %}</em>{% endif %}</span>
<ul class="report-details"> <ul class="report-details">

View File

@ -6,6 +6,13 @@
<!-- Content Sweep Actions --> <!-- Content Sweep Actions -->
<ul class="report-content-actions"> <ul class="report-content-actions">
{% if mod|hasPermission(config.mod.report_dismiss_content, report.board) %}
<!-- Dismiss All -->
<li class="report-content-action">
<a class="content-action-item content-action-available" title="{% trans 'Discard all abuse reports on this content' %}" href="{{uri_content_dismiss}}/{{token_content_dismiss}}">Dismiss All</a>
</li>
{% endif %}
{% if global and mod|hasPermission(config.mod.report_demote, report.board) %} {% if global and mod|hasPermission(config.mod.report_demote, report.board) %}
<!-- Demote All --> <!-- Demote All -->
<li class="report-action"> <li class="report-action">
@ -26,31 +33,23 @@
</li> </li>
{% endif %} {% endif %}
{% if mod|hasPermission(config.mod.report_dismiss_post, report.board) %} {% if mod|hasPermission(config.mod.clean, report.board) or mod|hasPermission(config.mod.clean_global, report.board) %}
<!-- Dismiss All -->
<li class="report-content-action"> <li class="report-content-action">
<a class="content-action-item content-action-available" title="{% trans 'Discard all abuse reports on this content' %}" href="{{uri_content_dismiss}}/{{token_content_dismiss}}">Dismiss All</a> Clean&nbsp;
</li> {% if mod|hasPermission(config.mod.clean, report.board) %}
{% endif %} <!-- 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>
{% if mod|hasPermission(config.mod.report_content_clean, report.board) %} {% endif %}
<!-- Clean Local --> {% if mod|hasPermission(config.mod.clean_global, report.board) %}
<li class="report-content-action"> &nbsp;
<a class="content-action-item content-action-available" title="{% trans 'Ignore and dismiss local abuse reports on this post for this board' %}" href="?/reports/clean/{{ content_board }}/{{ report.id }}/{{ token_clean }}">Clean (/{{ content_board }}/)</a> <!-- Clean Global -->
</li> <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>
{% endif %} &nbsp;
{% if mod|hasPermission(config.mod.clean, report.board) and mod|hasPermission(config.mod.clean_global, report.board) %}
{% if mod|hasPermission(config.mod.report_content_clean_global, report.board) %} <!-- Clean Local + Global -->
<!-- Clean 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>
<li class="report-content-action"> {% endif %}
<a class="content-action-item content-action-available" title="{% trans 'Ignore and demote global abuse reports on this post' %}" href="?/reports/cleanglocal/{{ report.id }}/{{ token_clean_global }}">Clean (Global)</a> {% endif %}
</li>
{% endif %}
{% if mod|hasPermission(config.mod.report_content_clean, report.board) and mod|hasPermission(config.mod.report_content_clean_global, report.board) %}
<!-- Clean Local + Global -->
<li class="report-content-action">
<a class="content-action-item content-action-available" title="{% trans 'Ignore and dismiss local AND global abuse reports on this post' %}" href="?/reports/content/{{ content_board }}/{{ token_clean_global }}">Clean (/{{ content_board }}/+Global)</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -1,4 +1,11 @@
{% if post.edited_at %} <div class="post_modified">
<br> {% if post.edited_at %}
<span class="unimportant edited">Post last edited at <em class="edited-time">{{ post.edited_at }}</em></span> <div class="content-status edited">{% trans 'Post last edited at' %} <em class="edited-time">{{ post.edited_at }}</em></div>
{% endif %} {% endif %}
{% if clean.clean_local == '1' %}
<div class="content-status clean-local">{% trans 'Board rules permit this content' %}</div>
{% endif %}
{% if clean.clean_global == '1' %}
<div class="content-status clean-global">{% trans 'Global rules permit this content' %}</div>
{% endif %}
</div>

View File

@ -22,8 +22,8 @@
{% if post.modifiers['ban message'] %} {% if post.modifiers['ban message'] %}
{{ config.mod.ban_message|sprintf(post.modifiers['ban message']) }} {{ config.mod.ban_message|sprintf(post.modifiers['ban message']) }}
{% endif %} {% endif %}
{% include 'post/edited_at.html' %}
</div> </div>
{% include 'post/edited_at.html' %}
</div> </div>
<br/> <br/>
{% endfilter %} {% endfilter %}

View File

@ -55,7 +55,6 @@
{% if post.modifiers['ban message'] %} {% if post.modifiers['ban message'] %}
{{ config.mod.ban_message|sprintf(post.modifiers['ban message']) }} {{ config.mod.ban_message|sprintf(post.modifiers['ban message']) }}
{% endif %} {% endif %}
{% include 'post/edited_at.html' %}
</div> </div>
{% if post.omitted or post.omitted_images %} {% if post.omitted or post.omitted_images %}
<span class="omitted"> <span class="omitted">
@ -78,6 +77,7 @@
{% endif %} {% trans %}omitted. Click reply to view.{% endtrans %} {% endif %} {% trans %}omitted. Click reply to view.{% endtrans %}
</span> </span>
{% endif %} {% endif %}
{% include 'post/edited_at.html' %}
{% if not index %} {% if not index %}
{% endif %} {% endif %}
</div>{% endfilter %} </div>{% endfilter %}