mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-23 05:23:17 +00:00
commit
ceba6b90a1
8 changed files with 605 additions and 248 deletions
|
@ -30,6 +30,7 @@
|
|||
- Updated Localization util, filter out .DS_Store. ([0107e8fd](https://github.com/pixelfed/pixelfed/commit/0107e8fd))
|
||||
- Updated PublicApiController, fix private account statuses api. Closes #2995. ([aa2dd26c](https://github.com/pixelfed/pixelfed/commit/aa2dd26c))
|
||||
- Updated Status model, use AccountService to generate urls instead of loading profile relation. ([2ae527c0](https://github.com/pixelfed/pixelfed/commit/2ae527c0))
|
||||
- Updated Autospam service, add mark all as read and mark all as not spam options and filter active, spam and not spam reports. ([ae8c7517](https://github.com/pixelfed/pixelfed/commit/ae8c7517))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.11.1 (2021-09-07)](https://github.com/pixelfed/pixelfed/compare/v0.11.0...v0.11.1)
|
||||
|
|
|
@ -3,16 +3,372 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Cache;
|
||||
use App\Report;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\StatusService;
|
||||
use App\{
|
||||
AccountInterstitial,
|
||||
Contact,
|
||||
Hashtag,
|
||||
Newsroom,
|
||||
OauthClient,
|
||||
Profile,
|
||||
Report,
|
||||
Status,
|
||||
Story,
|
||||
User
|
||||
};
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Services\StoryService;
|
||||
|
||||
trait AdminReportController
|
||||
{
|
||||
public function reports(Request $request)
|
||||
{
|
||||
$filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
|
||||
$page = $request->input('page') ?? 1;
|
||||
|
||||
$ai = Cache::remember('admin-dash:reports:ai-count', 3600, function() {
|
||||
return AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count();
|
||||
});
|
||||
|
||||
$spam = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
|
||||
return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
|
||||
});
|
||||
|
||||
$mailVerifications = Redis::scard('email:manual');
|
||||
|
||||
if($filter == 'open' && $page == 1) {
|
||||
$reports = Cache::remember('admin-dash:reports:list-cache', 300, function() use($page, $filter) {
|
||||
return Report::whereHas('status')
|
||||
->whereHas('reportedUser')
|
||||
->whereHas('reporter')
|
||||
->orderBy('created_at','desc')
|
||||
->when($filter, function($q, $filter) {
|
||||
return $filter == 'open' ?
|
||||
$q->whereNull('admin_seen') :
|
||||
$q->whereNotNull('admin_seen');
|
||||
})
|
||||
->paginate(6);
|
||||
});
|
||||
} else {
|
||||
$reports = Report::whereHas('status')
|
||||
->whereHas('reportedUser')
|
||||
->whereHas('reporter')
|
||||
->orderBy('created_at','desc')
|
||||
->when($filter, function($q, $filter) {
|
||||
return $filter == 'open' ?
|
||||
$q->whereNull('admin_seen') :
|
||||
$q->whereNotNull('admin_seen');
|
||||
})
|
||||
->paginate(6);
|
||||
}
|
||||
|
||||
return view('admin.reports.home', compact('reports', 'ai', 'spam', 'mailVerifications'));
|
||||
}
|
||||
|
||||
public function showReport(Request $request, $id)
|
||||
{
|
||||
$report = Report::findOrFail($id);
|
||||
return view('admin.reports.show', compact('report'));
|
||||
}
|
||||
|
||||
public function appeals(Request $request)
|
||||
{
|
||||
$appeals = AccountInterstitial::whereNotNull('appeal_requested_at')
|
||||
->whereNull('appeal_handled_at')
|
||||
->latest()
|
||||
->paginate(6);
|
||||
return view('admin.reports.appeals', compact('appeals'));
|
||||
}
|
||||
|
||||
public function showAppeal(Request $request, $id)
|
||||
{
|
||||
$appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
$meta = json_decode($appeal->meta);
|
||||
return view('admin.reports.show_appeal', compact('appeal', 'meta'));
|
||||
}
|
||||
|
||||
public function spam(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'tab' => 'sometimes|in:home,not-spam,spam,settings,custom,exemptions'
|
||||
]);
|
||||
|
||||
$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)
|
||||
{
|
||||
$appeal = AccountInterstitial::whereType('post.autospam')
|
||||
->findOrFail($id);
|
||||
$meta = json_decode($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)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => 'required|in:dismiss,approve,dismiss-all,approve-all'
|
||||
]);
|
||||
|
||||
$action = $request->input('action');
|
||||
$appeal = AccountInterstitial::whereType('post.autospam')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
|
||||
$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') {
|
||||
$appeal->is_spam = true;
|
||||
$appeal->appeal_handled_at = $now;
|
||||
$appeal->save();
|
||||
|
||||
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 == '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->is_nsfw = $meta->is_nsfw;
|
||||
$status->scope = 'public';
|
||||
$status->visibility = 'public';
|
||||
$status->save();
|
||||
|
||||
$appeal->is_spam = false;
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->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;
|
||||
}
|
||||
|
||||
public function updateAppeal(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => 'required|in:dismiss,approve'
|
||||
]);
|
||||
|
||||
$action = $request->input('action');
|
||||
$appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
|
||||
if($action == 'dismiss') {
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->save();
|
||||
Cache::forget('admin-dash:reports:ai-count');
|
||||
return redirect('/i/admin/reports/appeals');
|
||||
}
|
||||
|
||||
switch ($appeal->type) {
|
||||
case 'post.cw':
|
||||
$status = $appeal->status;
|
||||
$status->is_nsfw = false;
|
||||
$status->save();
|
||||
break;
|
||||
|
||||
case 'post.unlist':
|
||||
$status = $appeal->status;
|
||||
$status->scope = 'public';
|
||||
$status->visibility = 'public';
|
||||
$status->save();
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->save();
|
||||
StatusService::del($status->id);
|
||||
Cache::forget('admin-dash:reports:ai-count');
|
||||
|
||||
return redirect('/i/admin/reports/appeals');
|
||||
}
|
||||
|
||||
public function updateReport(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
|
|
|
@ -74,178 +74,6 @@ class AdminController extends Controller
|
|||
return view('admin.statuses.show', compact('status'));
|
||||
}
|
||||
|
||||
public function reports(Request $request)
|
||||
{
|
||||
$filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
|
||||
$page = $request->input('page') ?? 1;
|
||||
|
||||
$ai = Cache::remember('admin-dash:reports:ai-count', 3600, function() {
|
||||
return AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count();
|
||||
});
|
||||
|
||||
$spam = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
|
||||
return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
|
||||
});
|
||||
|
||||
$mailVerifications = Redis::scard('email:manual');
|
||||
|
||||
if($filter == 'open' && $page == 1) {
|
||||
$reports = Cache::remember('admin-dash:reports:list-cache', 300, function() use($page, $filter) {
|
||||
return Report::whereHas('status')
|
||||
->whereHas('reportedUser')
|
||||
->whereHas('reporter')
|
||||
->orderBy('created_at','desc')
|
||||
->when($filter, function($q, $filter) {
|
||||
return $filter == 'open' ?
|
||||
$q->whereNull('admin_seen') :
|
||||
$q->whereNotNull('admin_seen');
|
||||
})
|
||||
->paginate(6);
|
||||
});
|
||||
} else {
|
||||
$reports = Report::whereHas('status')
|
||||
->whereHas('reportedUser')
|
||||
->whereHas('reporter')
|
||||
->orderBy('created_at','desc')
|
||||
->when($filter, function($q, $filter) {
|
||||
return $filter == 'open' ?
|
||||
$q->whereNull('admin_seen') :
|
||||
$q->whereNotNull('admin_seen');
|
||||
})
|
||||
->paginate(6);
|
||||
}
|
||||
|
||||
return view('admin.reports.home', compact('reports', 'ai', 'spam', 'mailVerifications'));
|
||||
}
|
||||
|
||||
public function showReport(Request $request, $id)
|
||||
{
|
||||
$report = Report::findOrFail($id);
|
||||
return view('admin.reports.show', compact('report'));
|
||||
}
|
||||
|
||||
public function appeals(Request $request)
|
||||
{
|
||||
$appeals = AccountInterstitial::whereNotNull('appeal_requested_at')
|
||||
->whereNull('appeal_handled_at')
|
||||
->latest()
|
||||
->paginate(6);
|
||||
return view('admin.reports.appeals', compact('appeals'));
|
||||
}
|
||||
|
||||
public function showAppeal(Request $request, $id)
|
||||
{
|
||||
$appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
$meta = json_decode($appeal->meta);
|
||||
return view('admin.reports.show_appeal', compact('appeal', 'meta'));
|
||||
}
|
||||
|
||||
public function spam(Request $request)
|
||||
{
|
||||
$appeals = AccountInterstitial::whereType('post.autospam')
|
||||
->whereNull('appeal_handled_at')
|
||||
->latest()
|
||||
->paginate(6);
|
||||
return view('admin.reports.spam', compact('appeals'));
|
||||
}
|
||||
|
||||
public function showSpam(Request $request, $id)
|
||||
{
|
||||
$appeal = AccountInterstitial::whereType('post.autospam')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
$meta = json_decode($appeal->meta);
|
||||
return view('admin.reports.show_spam', compact('appeal', 'meta'));
|
||||
}
|
||||
|
||||
public function updateSpam(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => 'required|in:dismiss,approve'
|
||||
]);
|
||||
|
||||
$action = $request->input('action');
|
||||
$appeal = AccountInterstitial::whereType('post.autospam')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
|
||||
$meta = json_decode($appeal->meta);
|
||||
|
||||
if($action == 'dismiss') {
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->save();
|
||||
|
||||
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 redirect('/i/admin/reports/autospam');
|
||||
}
|
||||
|
||||
$status = $appeal->status;
|
||||
$status->is_nsfw = $meta->is_nsfw;
|
||||
$status->scope = 'public';
|
||||
$status->visibility = 'public';
|
||||
$status->save();
|
||||
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->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 redirect('/i/admin/reports/autospam');
|
||||
}
|
||||
|
||||
public function updateAppeal(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => 'required|in:dismiss,approve'
|
||||
]);
|
||||
|
||||
$action = $request->input('action');
|
||||
$appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
|
||||
->whereNull('appeal_handled_at')
|
||||
->findOrFail($id);
|
||||
|
||||
if($action == 'dismiss') {
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->save();
|
||||
Cache::forget('admin-dash:reports:ai-count');
|
||||
return redirect('/i/admin/reports/appeals');
|
||||
}
|
||||
|
||||
switch ($appeal->type) {
|
||||
case 'post.cw':
|
||||
$status = $appeal->status;
|
||||
$status->is_nsfw = false;
|
||||
$status->save();
|
||||
break;
|
||||
|
||||
case 'post.unlist':
|
||||
$status = $appeal->status;
|
||||
$status->scope = 'public';
|
||||
$status->visibility = 'public';
|
||||
$status->save();
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
|
||||
$appeal->appeal_handled_at = now();
|
||||
$appeal->save();
|
||||
StatusService::del($status->id);
|
||||
Cache::forget('admin-dash:reports:ai-count');
|
||||
|
||||
return redirect('/i/admin/reports/appeals');
|
||||
}
|
||||
|
||||
public function profiles(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
|
|
|
@ -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>
|
||||
|
||||
@if($ai || $spam || $mailVerifications)
|
||||
<div class="col-12 col-md-8 offset-md-2">
|
||||
<div class="mb-4">
|
||||
<a class="btn btn-outline-primary px-5 py-3 mr-3" href="/i/admin/reports/email-verifications">
|
||||
|
@ -33,7 +32,6 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($reports->count())
|
||||
<div class="col-12 col-md-8 offset-md-2">
|
||||
<div class="card shadow-none border">
|
||||
|
@ -43,7 +41,7 @@
|
|||
<div class="p-0">
|
||||
<div class="media d-flex align-items-center">
|
||||
<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>
|
||||
<div class="media-body">
|
||||
<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-header bg-light h5 font-weight-bold py-4">Unlisted + Content Warning</div>
|
||||
@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
|
||||
<div class="card-body">
|
||||
<div class="mt-2 p-3">
|
||||
|
@ -42,13 +42,15 @@
|
|||
@endif
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mt-3">
|
||||
<form method="post">
|
||||
@csrf
|
||||
<input type="hidden" name="action" value="dismiss">
|
||||
<button type="submit" class="btn btn-primary btn-block font-weight-bold mb-3">Mark as read</button>
|
||||
</form>
|
||||
<button type="button" class="btn btn-light border btn-block font-weight-bold mb-3" onclick="approveWarning()">Mark as not spam</button>
|
||||
<div class="card shadow-none border mt-5">
|
||||
@if($appeal->appeal_handled_at)
|
||||
@else
|
||||
<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="button" class="btn btn-light border btn-block font-weight-bold mb-3 action-btn" data-action="approve">Mark as not spam</button>
|
||||
<hr>
|
||||
<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>
|
||||
<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">
|
||||
@{{$appeal->user->username}} stats
|
||||
</div>
|
||||
|
@ -76,16 +78,43 @@
|
|||
|
||||
@push('scripts')
|
||||
<script type="text/javascript">
|
||||
function approveWarning() {
|
||||
if(window.confirm('Are you sure you want to mark this as not spam?') == true) {
|
||||
$('.action-btn').click((e) => {
|
||||
e.preventDefault();
|
||||
e.currentTarget.blur();
|
||||
|
||||
let type = e.currentTarget.getAttribute('data-action');
|
||||
|
||||
switch(type) {
|
||||
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: 'approve'
|
||||
action: type
|
||||
}).then(res => {
|
||||
window.location.href = '/i/admin/reports/autospam';
|
||||
location.href = '/i/admin/reports/autospam';
|
||||
}).catch(err => {
|
||||
swal('Oops!', 'An error occured, please try again later.', 'error');
|
||||
swal('Oops!', 'An error occured', 'error');
|
||||
console.log(err);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
|
@ -1,29 +1,132 @@
|
|||
@extends('admin.partial.template-full')
|
||||
|
||||
@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 class="row">
|
||||
<div class="col-12 col-md-3 mb-3">
|
||||
<div class="card border bg-primary text-white 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::whereNull('appeal_handled_at')->whereType('post.autospam')->count()}}</p>
|
||||
<p class="lead mb-0 font-weight-lighter">active cases</p>
|
||||
<div class="header bg-primary pb-3 mt-n4">
|
||||
<div class="container-fluid">
|
||||
<div class="header-body">
|
||||
<div class="row align-items-center py-4">
|
||||
<div class="col-lg-6 col-7">
|
||||
<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 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="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 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>
|
||||
<div class="col-12 col-md-8 offset-md-1">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-md-8">
|
||||
<ul class="nav nav-pills nav-fill mb-4">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{$tab=='home'?'active':''}}" href="/i/admin/reports/autospam">Active</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{$tab=='spam'?'active':''}}" href="/i/admin/reports/autospam?tab=spam">Spam</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{$tab=='not-spam'?'active':''}}" href="/i/admin/reports/autospam?tab=not-spam">Not Spam</a>
|
||||
</li>
|
||||
{{-- <li class="nav-item">
|
||||
<a class="nav-link" href="#">Closed</a>
|
||||
</li> --}}
|
||||
{{-- <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">
|
||||
|
@ -34,19 +137,15 @@
|
|||
<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">
|
||||
<div class="ml-2">
|
||||
<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 small font-weight-bold text-primary">{{$appeal->type}}</p>
|
||||
@if($appeal->item_type)
|
||||
<p class="mb-0 font-weight-bold">{{starts_with($appeal->item_type, 'App\\') ? explode('\\',$appeal->item_type)[1] : $appeal->item_type}}</p>
|
||||
@endif
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
<div class="d-inline-block">
|
||||
<p class="mb-0 small">
|
||||
|
@ -59,6 +158,7 @@
|
|||
</ul>
|
||||
<p>{!!$appeals->render()!!}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -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/bulk', 'AdminController@bulkUpdateReport');
|
||||
Route::get('reports/autospam/{id}', 'AdminController@showSpam');
|
||||
Route::post('reports/autospam/sync', 'AdminController@fixUncategorizedSpam');
|
||||
Route::post('reports/autospam/{id}', 'AdminController@updateSpam');
|
||||
Route::get('reports/autospam', 'AdminController@spam');
|
||||
Route::get('reports/appeals', 'AdminController@appeals');
|
||||
|
|
Loading…
Reference in a new issue