mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-29 01:33:16 +00:00
Update Autospam service, add mark all as read and mark all as not spam options and filter active, spam and not spamreports
This commit is contained in:
parent
dff3dad1c8
commit
ae8c751796
6 changed files with 428 additions and 84 deletions
|
@ -95,26 +95,155 @@ trait AdminReportController
|
||||||
|
|
||||||
public function spam(Request $request)
|
public function spam(Request $request)
|
||||||
{
|
{
|
||||||
$appeals = AccountInterstitial::whereType('post.autospam')
|
$this->validate($request, [
|
||||||
->whereNull('appeal_handled_at')
|
'tab' => 'sometimes|in:home,not-spam,spam,settings,custom,exemptions'
|
||||||
->latest()
|
]);
|
||||||
->paginate(6);
|
|
||||||
return view('admin.reports.spam', compact('appeals'));
|
$tab = $request->input('tab', 'home');
|
||||||
|
|
||||||
|
$openCount = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
|
||||||
|
return AccountInterstitial::whereType('post.autospam')
|
||||||
|
->whereNull('appeal_handled_at')
|
||||||
|
->count();
|
||||||
|
});
|
||||||
|
|
||||||
|
$monthlyCount = Cache::remember('admin-dash:reports:spam-count:30d', 43200, function() {
|
||||||
|
return AccountInterstitial::whereType('post.autospam')
|
||||||
|
->where('created_at', '>', now()->subMonth())
|
||||||
|
->count();
|
||||||
|
});
|
||||||
|
|
||||||
|
$totalCount = Cache::remember('admin-dash:reports:spam-count:total', 43200, function() {
|
||||||
|
return AccountInterstitial::whereType('post.autospam')->count();
|
||||||
|
});
|
||||||
|
|
||||||
|
$uncategorized = Cache::remember('admin-dash:reports:spam-sync', 3600, function() {
|
||||||
|
return AccountInterstitial::whereType('post.autospam')
|
||||||
|
->whereIsSpam(null)
|
||||||
|
->whereNotNull('appeal_handled_at')
|
||||||
|
->exists();
|
||||||
|
});
|
||||||
|
|
||||||
|
$avg = Cache::remember('admin-dash:reports:spam-count:avg', 43200, function() {
|
||||||
|
if(config('database.default') != 'mysql') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return AccountInterstitial::selectRaw('*, count(id) as counter')
|
||||||
|
->whereType('post.autospam')
|
||||||
|
->groupBy('user_id')
|
||||||
|
->get()
|
||||||
|
->avg('counter');
|
||||||
|
});
|
||||||
|
|
||||||
|
$avgOpen = Cache::remember('admin-dash:reports:spam-count:avgopen', 43200, function() {
|
||||||
|
if(config('database.default') != 'mysql') {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
$seconds = AccountInterstitial::selectRaw('DATE(created_at) AS start_date, AVG(TIME_TO_SEC(TIMEDIFF(appeal_handled_at, created_at))) AS timediff')->whereType('post.autospam')->whereNotNull('appeal_handled_at')->where('created_at', '>', now()->subMonth())->get();
|
||||||
|
if(!$seconds) {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
$mins = floor($seconds->avg('timediff') / 60);
|
||||||
|
|
||||||
|
if($mins < 60) {
|
||||||
|
return $mins . ' min(s)';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($mins < 2880) {
|
||||||
|
return floor($mins / 60) . ' hour(s)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return floor($mins / 60 / 24) . ' day(s)';
|
||||||
|
});
|
||||||
|
$avgCount = $totalCount && $avg ? floor($totalCount / $avg) : "0";
|
||||||
|
|
||||||
|
if(in_array($tab, ['home', 'spam', 'not-spam'])) {
|
||||||
|
$appeals = AccountInterstitial::whereType('post.autospam')
|
||||||
|
->when($tab, function($q, $tab) {
|
||||||
|
switch($tab) {
|
||||||
|
case 'home':
|
||||||
|
return $q->whereNull('appeal_handled_at');
|
||||||
|
break;
|
||||||
|
case 'spam':
|
||||||
|
return $q->whereIsSpam(true);
|
||||||
|
break;
|
||||||
|
case 'not-spam':
|
||||||
|
return $q->whereIsSpam(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->latest()
|
||||||
|
->paginate(6);
|
||||||
|
|
||||||
|
if($tab !== 'home') {
|
||||||
|
$appeals = $appeals->appends(['tab' => $tab]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$appeals = new class {
|
||||||
|
public function count() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return view('admin.reports.spam', compact('tab', 'appeals', 'openCount', 'monthlyCount', 'totalCount', 'avgCount', 'avgOpen', 'uncategorized'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showSpam(Request $request, $id)
|
public function showSpam(Request $request, $id)
|
||||||
{
|
{
|
||||||
$appeal = AccountInterstitial::whereType('post.autospam')
|
$appeal = AccountInterstitial::whereType('post.autospam')
|
||||||
->whereNull('appeal_handled_at')
|
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
$meta = json_decode($appeal->meta);
|
$meta = json_decode($appeal->meta);
|
||||||
return view('admin.reports.show_spam', compact('appeal', 'meta'));
|
return view('admin.reports.show_spam', compact('appeal', 'meta'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fixUncategorizedSpam(Request $request)
|
||||||
|
{
|
||||||
|
if(Cache::get('admin-dash:reports:spam-sync-active')) {
|
||||||
|
return redirect('/i/admin/reports/autospam');
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::put('admin-dash:reports:spam-sync-active', 1, 900);
|
||||||
|
|
||||||
|
AccountInterstitial::chunk(500, function($reports) {
|
||||||
|
foreach($reports as $report) {
|
||||||
|
if($report->item_type != 'App\Status') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($report->type != 'post.autospam') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($report->is_spam != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = StatusService::get($report->item_id, false);
|
||||||
|
if(!$status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope = $status['visibility'];
|
||||||
|
$report->is_spam = $scope == 'unlisted';
|
||||||
|
$report->in_violation = $report->is_spam;
|
||||||
|
$report->severity_index = 1;
|
||||||
|
$report->save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Cache::forget('admin-dash:reports:spam-sync');
|
||||||
|
return redirect('/i/admin/reports/autospam');
|
||||||
|
}
|
||||||
|
|
||||||
public function updateSpam(Request $request, $id)
|
public function updateSpam(Request $request, $id)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'action' => 'required|in:dismiss,approve'
|
'action' => 'required|in:dismiss,approve,dismiss-all,approve-all'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$action = $request->input('action');
|
$action = $request->input('action');
|
||||||
|
@ -123,15 +252,57 @@ trait AdminReportController
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
|
|
||||||
$meta = json_decode($appeal->meta);
|
$meta = json_decode($appeal->meta);
|
||||||
|
$res = ['status' => 'success'];
|
||||||
|
$now = now();
|
||||||
|
Cache::forget('admin-dash:reports:spam-count:total');
|
||||||
|
Cache::forget('admin-dash:reports:spam-count:30d');
|
||||||
|
|
||||||
if($action == 'dismiss') {
|
if($action == 'dismiss') {
|
||||||
$appeal->appeal_handled_at = now();
|
$appeal->is_spam = true;
|
||||||
|
$appeal->appeal_handled_at = $now;
|
||||||
$appeal->save();
|
$appeal->save();
|
||||||
|
|
||||||
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
||||||
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
||||||
Cache::forget('admin-dash:reports:spam-count');
|
Cache::forget('admin-dash:reports:spam-count');
|
||||||
return redirect('/i/admin/reports/autospam');
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($action == 'dismiss-all') {
|
||||||
|
AccountInterstitial::whereType('post.autospam')
|
||||||
|
->whereItemType('App\Status')
|
||||||
|
->whereNull('appeal_handled_at')
|
||||||
|
->whereUserId($appeal->user_id)
|
||||||
|
->update(['appeal_handled_at' => $now, 'is_spam' => true]);
|
||||||
|
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
||||||
|
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
||||||
|
Cache::forget('admin-dash:reports:spam-count');
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($action == 'approve-all') {
|
||||||
|
AccountInterstitial::whereType('post.autospam')
|
||||||
|
->whereItemType('App\Status')
|
||||||
|
->whereNull('appeal_handled_at')
|
||||||
|
->whereUserId($appeal->user_id)
|
||||||
|
->get()
|
||||||
|
->each(function($report) use($meta) {
|
||||||
|
$report->is_spam = false;
|
||||||
|
$report->appeal_handled_at = now();
|
||||||
|
$report->save();
|
||||||
|
$status = Status::find($report->item_id);
|
||||||
|
if($status) {
|
||||||
|
$status->is_nsfw = $meta->is_nsfw;
|
||||||
|
$status->scope = 'public';
|
||||||
|
$status->visibility = 'public';
|
||||||
|
$status->save();
|
||||||
|
StatusService::del($status->id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
|
||||||
|
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
||||||
|
Cache::forget('admin-dash:reports:spam-count');
|
||||||
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
$status = $appeal->status;
|
$status = $appeal->status;
|
||||||
|
@ -140,6 +311,7 @@ trait AdminReportController
|
||||||
$status->visibility = 'public';
|
$status->visibility = 'public';
|
||||||
$status->save();
|
$status->save();
|
||||||
|
|
||||||
|
$appeal->is_spam = false;
|
||||||
$appeal->appeal_handled_at = now();
|
$appeal->appeal_handled_at = now();
|
||||||
$appeal->save();
|
$appeal->save();
|
||||||
|
|
||||||
|
@ -149,7 +321,7 @@ trait AdminReportController
|
||||||
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
|
||||||
Cache::forget('admin-dash:reports:spam-count');
|
Cache::forget('admin-dash:reports:spam-count');
|
||||||
|
|
||||||
return redirect('/i/admin/reports/autospam');
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateAppeal(Request $request, $id)
|
public function updateAppeal(Request $request, $id)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddActionToAccountInterstitialsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('account_interstitials', function (Blueprint $table) {
|
||||||
|
$table->tinyInteger('severity_index')->unsigned()->nullable()->index();
|
||||||
|
$table->boolean('is_spam')->nullable()->index()->after('item_type');
|
||||||
|
$table->boolean('in_violation')->nullable()->index()->after('is_spam');
|
||||||
|
$table->unsignedInteger('violation_id')->nullable()->index()->after('in_violation');
|
||||||
|
$table->boolean('email_notify')->nullable()->index()->after('violation_id');
|
||||||
|
$table->bigInteger('thread_id')->unsigned()->unique()->nullable();
|
||||||
|
$table->timestamp('emailed_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('account_interstitials', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('severity_index');
|
||||||
|
$table->dropColumn('is_spam');
|
||||||
|
$table->dropColumn('in_violation');
|
||||||
|
$table->dropColumn('violation_id');
|
||||||
|
$table->dropColumn('email_notify');
|
||||||
|
$table->dropColumn('thread_id');
|
||||||
|
$table->dropColumn('emailed_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($ai || $spam || $mailVerifications)
|
|
||||||
<div class="col-12 col-md-8 offset-md-2">
|
<div class="col-12 col-md-8 offset-md-2">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<a class="btn btn-outline-primary px-5 py-3 mr-3" href="/i/admin/reports/email-verifications">
|
<a class="btn btn-outline-primary px-5 py-3 mr-3" href="/i/admin/reports/email-verifications">
|
||||||
|
@ -33,7 +32,6 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
|
||||||
@if($reports->count())
|
@if($reports->count())
|
||||||
<div class="col-12 col-md-8 offset-md-2">
|
<div class="col-12 col-md-8 offset-md-2">
|
||||||
<div class="card shadow-none border">
|
<div class="card shadow-none border">
|
||||||
|
@ -43,7 +41,7 @@
|
||||||
<div class="p-0">
|
<div class="p-0">
|
||||||
<div class="media d-flex align-items-center">
|
<div class="media d-flex align-items-center">
|
||||||
<a class="text-decoration-none" href="{{$report->url()}}">
|
<a class="text-decoration-none" href="{{$report->url()}}">
|
||||||
<img src="{{$report->status->media->count() ? $report->status->thumb(true) : '/storage/no-preview.png'}}" width="64" height="64" class="rounded border shadow mr-3" style="object-fit: cover">
|
<img src="{{$report->status->media && $report->status->media->count() ? $report->status->thumb(true) : '/storage/no-preview.png'}}" width="64" height="64" class="rounded border shadow mr-3" style="object-fit: cover">
|
||||||
</a>
|
</a>
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<p class="mb-1 small"><span class="font-weight-bold text-uppercase text-danger">{{$report->type}}</span></p>
|
<p class="mb-1 small"><span class="font-weight-bold text-uppercase text-danger">{{$report->type}}</span></p>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<div class="card shadow-none border">
|
<div class="card shadow-none border">
|
||||||
<div class="card-header bg-light h5 font-weight-bold py-4">Unlisted + Content Warning</div>
|
<div class="card-header bg-light h5 font-weight-bold py-4">Unlisted + Content Warning</div>
|
||||||
@if($appeal->has_media)
|
@if($appeal->has_media)
|
||||||
<img class="card-img-top border-bottom" src="{{$appeal->status->thumb(true)}}">
|
<img class="card-img-top border-bottom" src="{{$appeal->status->thumb(true)}}" style="max-height: 40vh;object-fit: contain;">
|
||||||
@endif
|
@endif
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="mt-2 p-3">
|
<div class="mt-2 p-3">
|
||||||
|
@ -42,13 +42,15 @@
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4 mt-3">
|
<div class="col-12 col-md-4 mt-3">
|
||||||
<form method="post">
|
@if($appeal->appeal_handled_at)
|
||||||
@csrf
|
@else
|
||||||
<input type="hidden" name="action" value="dismiss">
|
<button type="button" class="btn btn-primary border btn-block font-weight-bold mb-3 action-btn" data-action="dismiss">Mark as read</button>
|
||||||
<button type="submit" class="btn btn-primary btn-block font-weight-bold mb-3">Mark as read</button>
|
<button type="button" class="btn btn-light border btn-block font-weight-bold mb-3 action-btn" data-action="approve">Mark as not spam</button>
|
||||||
</form>
|
<hr>
|
||||||
<button type="button" class="btn btn-light border btn-block font-weight-bold mb-3" onclick="approveWarning()">Mark as not spam</button>
|
<button type="button" class="btn btn-default border btn-block font-weight-bold mb-3 action-btn" data-action="dismiss-all">Mark all as read</button>
|
||||||
<div class="card shadow-none border mt-5">
|
<button type="button" class="btn btn-light border btn-block font-weight-bold mb-3 action-btn mb-5" data-action="approve-all">Mark all as not spam</button>
|
||||||
|
@endif
|
||||||
|
<div class="card shadow-none border">
|
||||||
<div class="card-header text-center font-weight-bold bg-light">
|
<div class="card-header text-center font-weight-bold bg-light">
|
||||||
@{{$appeal->user->username}} stats
|
@{{$appeal->user->username}} stats
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,16 +78,43 @@
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function approveWarning() {
|
$('.action-btn').click((e) => {
|
||||||
if(window.confirm('Are you sure you want to mark this as not spam?') == true) {
|
e.preventDefault();
|
||||||
axios.post(window.location.href, {
|
e.currentTarget.blur();
|
||||||
action: 'approve'
|
|
||||||
}).then(res => {
|
let type = e.currentTarget.getAttribute('data-action');
|
||||||
window.location.href = '/i/admin/reports/autospam';
|
|
||||||
}).catch(err => {
|
switch(type) {
|
||||||
swal('Oops!', 'An error occured, please try again later.', 'error');
|
case 'dismiss':
|
||||||
});
|
break;
|
||||||
|
|
||||||
|
case 'approve':
|
||||||
|
if(!window.confirm('Are you sure you want to approve this post?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'dismiss-all':
|
||||||
|
if(!window.confirm('Are you sure you want to dismiss all autospam reports?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'approve-all':
|
||||||
|
if(!window.confirm('Are you sure you want to approve this post and all other posts by this account?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
axios.post(window.location.href, {
|
||||||
|
action: type
|
||||||
|
}).then(res => {
|
||||||
|
location.href = '/i/admin/reports/autospam';
|
||||||
|
}).catch(err => {
|
||||||
|
swal('Oops!', 'An error occured', 'error');
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
|
@ -1,63 +1,163 @@
|
||||||
@extends('admin.partial.template-full')
|
@extends('admin.partial.template-full')
|
||||||
|
|
||||||
@section('section')
|
@section('section')
|
||||||
<div class="title mb-4">
|
|
||||||
<h3 class="font-weight-bold d-inline-block">Autospam</h3>
|
|
||||||
<p class="lead">Posts flagged as spam</p>
|
|
||||||
<span class="float-right">
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="header bg-primary pb-3 mt-n4">
|
||||||
<div class="col-12 col-md-3 mb-3">
|
<div class="container-fluid">
|
||||||
<div class="card border bg-primary text-white rounded-pill shadow">
|
<div class="header-body">
|
||||||
<div class="card-body pl-4 ml-3">
|
<div class="row align-items-center py-4">
|
||||||
<p class="h1 font-weight-bold mb-1" style="font-weight: 700">{{App\AccountInterstitial::whereNull('appeal_handled_at')->whereType('post.autospam')->count()}}</p>
|
<div class="col-lg-6 col-7">
|
||||||
<p class="lead mb-0 font-weight-lighter">active cases</p>
|
<p class="display-1 text-white d-inline-block mb-0">Autospam</p>
|
||||||
|
<p class="lead text-white mb-0 mt-n3">Automated Spam Detection</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="row">
|
||||||
|
<div class="col-xl-3 col-md-6">
|
||||||
|
<div class="card card-stats">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h5 class="card-title text-uppercase text-muted mb-0">Active Reports</h5>
|
||||||
|
<span class="h2 font-weight-bold mb-0">{{$openCount}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="icon icon-shape bg-gradient-primary text-white rounded-circle shadow">
|
||||||
|
<i class="far fa-exclamation-circle"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-3 mb-0 text-sm">
|
||||||
|
<span class="text-success mr-2"><i class="fa fa-arrow-up"></i> {{$monthlyCount}}</span>
|
||||||
|
<span class="text-nowrap">in last 30 days</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-3 col-md-6">
|
||||||
|
<div class="card card-stats">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h5 class="card-title text-uppercase text-muted mb-0">Avg Response Time</h5>
|
||||||
|
<span class="h2 font-weight-bold mb-0">{{$avgOpen}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="icon icon-shape bg-gradient-primary text-white rounded-circle shadow">
|
||||||
|
<i class="far fa-clock"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-3 mb-0 text-sm">
|
||||||
|
<span class="text-nowrap">in last 30 days</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($uncategorized)
|
||||||
|
<div class="col-xl-3 col-md-6">
|
||||||
|
<div class="card card-stats">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h5 class="card-title text-uppercase text-muted mb-0">Uncategorized</h5>
|
||||||
|
<span class="h2 font-weight-bold mb-0">Reports Found</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="icon icon-shape bg-danger text-white rounded-circle shadow">
|
||||||
|
<i class="far fa-exclamation-triangle"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form action="/i/admin/reports/autospam/sync" method="post" class="mt-2 p-0">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-danger py-1 px-2"><i class="far fa-ambulance mr-2"></i> Manual Fix</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="col-xl-2 col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<h5 class="text-light text-uppercase mb-0">Total Reports</h5>
|
||||||
|
<span class="text-white h2 font-weight-bold mb-0">{{$totalCount}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<h5 class="text-light text-uppercase mb-0">Reports per user</h5>
|
||||||
|
<span class="text-white h2 font-weight-bold mb-0">{{$avgCount}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-3 card border bg-warning text-dark rounded-pill shadow">
|
|
||||||
<div class="card-body pl-4 ml-3">
|
|
||||||
<p class="h1 font-weight-bold mb-1" style="font-weight: 700">{{App\AccountInterstitial::whereType('post.autospam')->count()}}</p>
|
|
||||||
<p class="lead mb-0 font-weight-lighter">total cases</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-8 offset-md-1">
|
</div>
|
||||||
<ul class="list-group">
|
|
||||||
@if($appeals->count() == 0)
|
<div class="container-fluid mt-4">
|
||||||
<li class="list-group-item text-center py-5">
|
<div class="row justify-content-center">
|
||||||
<p class="mb-0 py-5 font-weight-bold">No autospam cases found!</p>
|
<div class="col-12 col-md-8">
|
||||||
</li>
|
<ul class="nav nav-pills nav-fill mb-4">
|
||||||
@endif
|
<li class="nav-item">
|
||||||
@foreach($appeals as $appeal)
|
<a class="nav-link {{$tab=='home'?'active':''}}" href="/i/admin/reports/autospam">Active</a>
|
||||||
<a class="list-group-item text-decoration-none text-dark" href="/i/admin/reports/autospam/{{$appeal->id}}">
|
</li>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<li class="nav-item">
|
||||||
<div class="d-flex align-items-center">
|
<a class="nav-link {{$tab=='spam'?'active':''}}" href="/i/admin/reports/autospam?tab=spam">Spam</a>
|
||||||
<img src="{{$appeal->has_media ? $appeal->status->thumb(true) : '/storage/no-preview.png'}}" width="64" height="64" class="rounded border">
|
</li>
|
||||||
<div class="ml-2">
|
<li class="nav-item">
|
||||||
<span class="d-inline-block text-truncate">
|
<a class="nav-link {{$tab=='not-spam'?'active':''}}" href="/i/admin/reports/autospam?tab=not-spam">Not Spam</a>
|
||||||
<p class="mb-0 small font-weight-bold text-primary">{{$appeal->type}}</p>
|
</li>
|
||||||
@if($appeal->item_type)
|
{{-- <li class="nav-item">
|
||||||
<p class="mb-0 font-weight-bold">{{starts_with($appeal->item_type, 'App\\') ? explode('\\',$appeal->item_type)[1] : $appeal->item_type}}</p>
|
<a class="nav-link" href="#">Closed</a>
|
||||||
@endif
|
</li> --}}
|
||||||
</span>
|
{{-- <li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">Review</a>
|
||||||
|
</li> --}}
|
||||||
|
{{-- <li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">Train</a>
|
||||||
|
</li> --}}
|
||||||
|
{{-- <li class="nav-item">
|
||||||
|
<a class="nav-link {{$tab=='exemptions'?'active':''}}" href="/i/admin/reports/autospam?tab=exemptions">Exemptions</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{$tab=='custom'?'active':''}}" href="/i/admin/reports/autospam?tab=custom">Custom</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" style="max-width: 50px;">
|
||||||
|
<a class="nav-link {{$tab=='settings'?'active':''}}" href="/i/admin/reports/autospam?tab=settings"><i class="far fa-cog"></i></a>
|
||||||
|
</li> --}}
|
||||||
|
</ul>
|
||||||
|
<ul class="list-group">
|
||||||
|
@if($appeals->count() == 0)
|
||||||
|
<li class="list-group-item text-center py-5">
|
||||||
|
<p class="mb-0 py-5 font-weight-bold">No autospam cases found!</p>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
@foreach($appeals as $appeal)
|
||||||
|
<a class="list-group-item text-decoration-none text-dark" href="/i/admin/reports/autospam/{{$appeal->id}}">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<img src="{{$appeal->has_media ? $appeal->status->thumb(true) : '/storage/no-preview.png'}}" width="64" height="64" class="rounded border" onerror="this.onerror=null;this.src='/storage/no-preview.png';">
|
||||||
|
<div class="ml-3">
|
||||||
|
<span class="d-inline-block text-truncate">
|
||||||
|
<p class="mb-0 font-weight-bold">@{{$appeal->user->username}}</p>
|
||||||
|
<p class="mb-0 small text-muted font-weight-bold">{{$appeal->created_at->diffForHumans(null, null, true)}}</p>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-block">
|
||||||
|
</div>
|
||||||
|
<div class="d-inline-block">
|
||||||
|
<p class="mb-0 small">
|
||||||
|
<i class="fas fa-chevron-right fa-2x text-lighter"></i>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-block">
|
</a>
|
||||||
<p class="mb-0 font-weight-bold">@{{$appeal->user->username}}</p>
|
@endforeach
|
||||||
<p class="mb-0 small text-muted font-weight-bold">{{$appeal->created_at->diffForHumans(null, null, true)}}</p>
|
</ul>
|
||||||
</div>
|
<p>{!!$appeals->render()!!}</p>
|
||||||
<div class="d-inline-block">
|
</div>
|
||||||
<p class="mb-0 small">
|
|
||||||
<i class="fas fa-chevron-right fa-2x text-lighter"></i>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
@endforeach
|
|
||||||
</ul>
|
|
||||||
<p>{!!$appeals->render()!!}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
|
||||||
Route::post('reports/show/{id}', 'AdminController@updateReport');
|
Route::post('reports/show/{id}', 'AdminController@updateReport');
|
||||||
Route::post('reports/bulk', 'AdminController@bulkUpdateReport');
|
Route::post('reports/bulk', 'AdminController@bulkUpdateReport');
|
||||||
Route::get('reports/autospam/{id}', 'AdminController@showSpam');
|
Route::get('reports/autospam/{id}', 'AdminController@showSpam');
|
||||||
|
Route::post('reports/autospam/sync', 'AdminController@fixUncategorizedSpam');
|
||||||
Route::post('reports/autospam/{id}', 'AdminController@updateSpam');
|
Route::post('reports/autospam/{id}', 'AdminController@updateSpam');
|
||||||
Route::get('reports/autospam', 'AdminController@spam');
|
Route::get('reports/autospam', 'AdminController@spam');
|
||||||
Route::get('reports/appeals', 'AdminController@appeals');
|
Route::get('reports/appeals', 'AdminController@appeals');
|
||||||
|
|
Loading…
Reference in a new issue