From 767522a85c1d9c29dd05d2b3b6c502379dc073d8 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 26 Feb 2024 21:33:10 -0700 Subject: [PATCH 1/3] Update AdminReports, add story reports and fix cs --- .../assets/components/admin/AdminReports.vue | 1403 +++++++++-------- 1 file changed, 732 insertions(+), 671 deletions(-) diff --git a/resources/assets/components/admin/AdminReports.vue b/resources/assets/components/admin/AdminReports.vue index fdd11b01..7bfcd10a 100644 --- a/resources/assets/components/admin/AdminReports.vue +++ b/resources/assets/components/admin/AdminReports.vue @@ -9,15 +9,15 @@
-
+
Active Reports
- {{ prettyCount(stats.open) }} + class="text-white h2 font-weight-bold mb-0 human-size" + data-toggle="tooltip" + data-placement="bottom" + :title="stats.open + ' open reports'"> + {{ prettyCount(stats.open) }}
@@ -26,11 +26,11 @@
Active Spam Detections
{{ prettyCount(stats.autospam_open) }} + class="text-white h2 font-weight-bold mb-0 human-size" + data-toggle="tooltip" + data-placement="bottom" + :title="stats.autospam_open + ' open spam detections'" + >{{ prettyCount(stats.autospam_open) }}
@@ -38,11 +38,11 @@
Total Reports
{{ prettyCount(stats.total) }} + class="text-white h2 font-weight-bold mb-0 human-size" + data-toggle="tooltip" + data-placement="bottom" + :title="stats.total + ' total reports'" + >{{ prettyCount(stats.total) }}
@@ -51,11 +51,11 @@
Total Spam Detections
- {{ prettyCount(stats.autospam) }} + class="text-white h2 font-weight-bold mb-0 human-size" + data-toggle="tooltip" + data-placement="bottom" + :title="stats.autospam + ' total spam detections'"> + {{ prettyCount(stats.autospam) }}
@@ -75,73 +75,73 @@ @@ -151,10 +151,10 @@ - - - - + + + + @@ -167,49 +167,49 @@ - + @@ -218,17 +218,17 @@
IDReportReported AccountReported ByIDReportReported AccountReported By Created View Report
-

-
- -
- - -
-

@{{report.reported.username}}

-
- {{report.reported.followers_count}} Followers - · - Joined {{ timeAgo(report.reported.created_at) }} -
-
-
-
+

- -
- + +
+ -
-

@{{report.reporter.username}}

-
- {{report.reporter.followers_count}} Followers - · - Joined {{ timeAgo(report.reporter.created_at) }} -
-
-
-
+
+

@{{report.reported.username}}

+
+ {{report.reported.followers_count}} Followers + · + Joined {{ timeAgo(report.reported.created_at) }} +
+
+
+ +
+ +
+ + +
+

@{{report.reporter.username}}

+
+ {{report.reporter.followers_count}} Followers + · + Joined {{ timeAgo(report.reporter.created_at) }} +
+
+
+
{{ timeAgo(report.created_at) }} View
-
-
-

-

{{ tabIndex === 0 ? 'No Active Reports Found!' : 'No Closed Reports Found!' }}

-
-
+
+
+

+

{{ tabIndex === 0 ? 'No Active Reports Found!' : 'No Closed Reports Found!' }}

+
+
-
- -
- -
+
+ +
-
-
- -
+
+ +
-
-
- -
+
+ +
-
From a16309ac188eee92faecf21afca41d40cda93a3f Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 26 Feb 2024 21:39:09 -0700 Subject: [PATCH 2/3] Update AdminReportController, add story report support --- .../Admin/AdminReportController.php | 1921 +++++++++-------- 1 file changed, 1014 insertions(+), 907 deletions(-) diff --git a/app/Http/Controllers/Admin/AdminReportController.php b/app/Http/Controllers/Admin/AdminReportController.php index ac238f28..2ad2bab8 100644 --- a/app/Http/Controllers/Admin/AdminReportController.php +++ b/app/Http/Controllers/Admin/AdminReportController.php @@ -2,461 +2,468 @@ namespace App\Http\Controllers\Admin; +use App\AccountInterstitial; +use App\Http\Resources\AdminReport; +use App\Http\Resources\AdminSpamReport; +use App\Jobs\DeletePipeline\DeleteAccountPipeline; +use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline; +use App\Jobs\StatusPipeline\RemoteStatusDelete; +use App\Jobs\StatusPipeline\StatusDelete; +use App\Jobs\StoryPipeline\StoryDelete; +use App\Notification; +use App\Profile; +use App\Report; +use App\Services\AccountService; +use App\Services\ModLogService; +use App\Services\NetworkTimelineService; +use App\Services\NotificationService; +use App\Services\PublicTimelineService; +use App\Services\StatusService; +use App\Status; +use App\Story; +use App\User; use Cache; use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Redis; -use App\Services\AccountService; -use App\Services\StatusService; -use App\{ - AccountInterstitial, - Contact, - Hashtag, - Newsroom, - Notification, - OauthClient, - Profile, - Report, - Status, - Story, - User -}; -use Illuminate\Validation\Rule; -use App\Services\StoryService; -use App\Services\ModLogService; -use App\Jobs\DeletePipeline\DeleteAccountPipeline; -use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline; -use App\Jobs\StatusPipeline\StatusDelete; -use App\Jobs\StatusPipeline\RemoteStatusDelete; -use App\Http\Resources\AdminReport; -use App\Http\Resources\AdminSpamReport; -use App\Services\NotificationService; -use App\Services\PublicTimelineService; -use App\Services\NetworkTimelineService; trait AdminReportController { - public function reports(Request $request) - { - $filter = $request->input('filter') == 'closed' ? 'closed' : 'open'; - $page = $request->input('page') ?? 1; + 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(); - }); + $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(); - }); + $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'); + $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); - } + if ($filter == 'open' && $page == 1) { + $reports = Cache::remember('admin-dash:reports:list-cache', 300, function () use ($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')); - } + return view('admin.reports.home', compact('reports', 'ai', 'spam', 'mailVerifications')); + } - public function showReport(Request $request, $id) - { - $report = Report::with('status')->findOrFail($id); - if($request->has('ref') && $request->input('ref') == 'email') { - return redirect('/i/admin/reports?tab=report&id=' . $report->id); - } - return view('admin.reports.show', compact('report')); - } + public function showReport(Request $request, $id) + { + $report = Report::with('status')->findOrFail($id); + if ($request->has('ref') && $request->input('ref') == 'email') { + return redirect('/i/admin/reports?tab=report&id='.$report->id); + } - 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')); - } + return view('admin.reports.show', compact('report')); + } - 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 appeals(Request $request) + { + $appeals = AccountInterstitial::whereNotNull('appeal_requested_at') + ->whereNull('appeal_handled_at') + ->latest() + ->paginate(6); - public function spam(Request $request) - { - $this->validate($request, [ - 'tab' => 'sometimes|in:home,not-spam,spam,settings,custom,exemptions' - ]); + return view('admin.reports.appeals', compact('appeals')); + } - $tab = $request->input('tab', 'home'); + public function showAppeal(Request $request, $id) + { + $appeal = AccountInterstitial::whereNotNull('appeal_requested_at') + ->whereNull('appeal_handled_at') + ->findOrFail($id); + $meta = json_decode($appeal->meta); - $openCount = Cache::remember('admin-dash:reports:spam-count', 3600, function() { - return AccountInterstitial::whereType('post.autospam') - ->whereNull('appeal_handled_at') - ->count(); - }); + return view('admin.reports.show_appeal', compact('appeal', 'meta')); + } - $monthlyCount = Cache::remember('admin-dash:reports:spam-count:30d', 43200, function() { - return AccountInterstitial::whereType('post.autospam') - ->where('created_at', '>', now()->subMonth()) - ->count(); - }); + public function spam(Request $request) + { + $this->validate($request, [ + 'tab' => 'sometimes|in:home,not-spam,spam,settings,custom,exemptions', + ]); - $totalCount = Cache::remember('admin-dash:reports:spam-count:total', 43200, function() { - return AccountInterstitial::whereType('post.autospam')->count(); - }); + $tab = $request->input('tab', 'home'); - $uncategorized = Cache::remember('admin-dash:reports:spam-sync', 3600, function() { - return AccountInterstitial::whereType('post.autospam') - ->whereIsSpam(null) - ->whereNotNull('appeal_handled_at') - ->exists(); - }); + $openCount = Cache::remember('admin-dash:reports:spam-count', 3600, function () { + return AccountInterstitial::whereType('post.autospam') + ->whereNull('appeal_handled_at') + ->count(); + }); - $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'); - }); + $monthlyCount = Cache::remember('admin-dash:reports:spam-count:30d', 43200, function () { + return AccountInterstitial::whereType('post.autospam') + ->where('created_at', '>', now()->subMonth()) + ->count(); + }); - $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); + $totalCount = Cache::remember('admin-dash:reports:spam-count:total', 43200, function () { + return AccountInterstitial::whereType('post.autospam')->count(); + }); - if($mins < 60) { - return $mins . ' min(s)'; - } + $uncategorized = Cache::remember('admin-dash:reports:spam-sync', 3600, function () { + return AccountInterstitial::whereType('post.autospam') + ->whereIsSpam(null) + ->whereNotNull('appeal_handled_at') + ->exists(); + }); - if($mins < 2880) { - return floor($mins / 60) . ' hour(s)'; - } + $avg = Cache::remember('admin-dash:reports:spam-count:avg', 43200, function () { + if (config('database.default') != 'mysql') { + return 0; + } - return floor($mins / 60 / 24) . ' day(s)'; - }); - $avgCount = $totalCount && $avg ? floor($totalCount / $avg) : "0"; + return AccountInterstitial::selectRaw('*, count(id) as counter') + ->whereType('post.autospam') + ->groupBy('user_id') + ->get() + ->avg('counter'); + }); - 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); + $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($tab !== 'home') { - $appeals = $appeals->appends(['tab' => $tab]); - } - } else { - $appeals = new class { - public function count() { - return 0; - } + if ($mins < 60) { + return $mins.' min(s)'; + } - public function render() { - return; - } - }; - } + if ($mins < 2880) { + return floor($mins / 60).' hour(s)'; + } + return floor($mins / 60 / 24).' day(s)'; + }); + $avgCount = $totalCount && $avg ? floor($totalCount / $avg) : '0'; - return view('admin.reports.spam', compact('tab', 'appeals', 'openCount', 'monthlyCount', 'totalCount', 'avgCount', 'avgOpen', 'uncategorized')); - } + 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); - public function showSpam(Request $request, $id) - { - $appeal = AccountInterstitial::whereType('post.autospam') - ->findOrFail($id); - if($request->has('ref') && $request->input('ref') == 'email') { - return redirect('/i/admin/reports?tab=autospam&id=' . $appeal->id); - } - $meta = json_decode($appeal->meta); - return view('admin.reports.show_spam', compact('appeal', 'meta')); - } + if ($tab !== 'home') { + $appeals = $appeals->appends(['tab' => $tab]); + } + } else { + $appeals = new class + { + public function count() + { + return 0; + } - public function fixUncategorizedSpam(Request $request) - { - if(Cache::get('admin-dash:reports:spam-sync-active')) { - return redirect('/i/admin/reports/autospam'); - } + public function render() + { - 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; - } + return view('admin.reports.spam', compact('tab', 'appeals', 'openCount', 'monthlyCount', 'totalCount', 'avgCount', 'avgOpen', 'uncategorized')); + } - if($report->type != 'post.autospam') { - continue; - } + public function showSpam(Request $request, $id) + { + $appeal = AccountInterstitial::whereType('post.autospam') + ->findOrFail($id); + if ($request->has('ref') && $request->input('ref') == 'email') { + return redirect('/i/admin/reports?tab=autospam&id='.$appeal->id); + } + $meta = json_decode($appeal->meta); - if($report->is_spam != null) { - continue; - } + return view('admin.reports.show_spam', compact('appeal', 'meta')); + } - $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(); - } - }); + public function fixUncategorizedSpam(Request $request) + { + if (Cache::get('admin-dash:reports:spam-sync-active')) { + return redirect('/i/admin/reports/autospam'); + } - Cache::forget('admin-dash:reports:spam-sync'); - return redirect('/i/admin/reports/autospam'); - } + Cache::put('admin-dash:reports:spam-sync-active', 1, 900); - public function updateSpam(Request $request, $id) - { - $this->validate($request, [ - 'action' => 'required|in:dismiss,approve,dismiss-all,approve-all,delete-account,mark-spammer' - ]); + AccountInterstitial::chunk(500, function ($reports) { + foreach ($reports as $report) { + if ($report->item_type != 'App\Status') { + continue; + } - $action = $request->input('action'); - $appeal = AccountInterstitial::whereType('post.autospam') - ->whereNull('appeal_handled_at') - ->findOrFail($id); + if ($report->type != 'post.autospam') { + continue; + } - $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 ($report->is_spam != null) { + continue; + } - if($action == 'delete-account') { - if(config('pixelfed.account_deletion') == false) { - abort(404); - } + $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(); + } + }); - $user = User::findOrFail($appeal->user_id); - $profile = $user->profile; + Cache::forget('admin-dash:reports:spam-sync'); - if($user->is_admin == true) { - $mid = $request->user()->id; - abort_if($user->id < $mid, 403); - } + return redirect('/i/admin/reports/autospam'); + } - $ts = now()->addMonth(); - $user->status = 'delete'; - $profile->status = 'delete'; - $user->delete_after = $ts; - $profile->delete_after = $ts; - $user->save(); - $profile->save(); + public function updateSpam(Request $request, $id) + { + $this->validate($request, [ + 'action' => 'required|in:dismiss,approve,dismiss-all,approve-all,delete-account,mark-spammer', + ]); - ModLogService::boot() - ->objectUid($user->id) - ->objectId($user->id) - ->objectType('App\User::class') - ->user($request->user()) - ->action('admin.user.delete') - ->accessLevel('admin') - ->save(); + $action = $request->input('action'); + $appeal = AccountInterstitial::whereType('post.autospam') + ->whereNull('appeal_handled_at') + ->findOrFail($id); - Cache::forget('profiles:private'); - DeleteAccountPipeline::dispatch($user); - return; - } + $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(); + if ($action == 'delete-account') { + if (config('pixelfed.account_deletion') == false) { + abort(404); + } - 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; - } + $user = User::findOrFail($appeal->user_id); + $profile = $user->profile; - 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 ($user->is_admin == true) { + $mid = $request->user()->id; + abort_if($user->id < $mid, 403); + } - 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, 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; - } + $ts = now()->addMonth(); + $user->status = 'delete'; + $profile->status = 'delete'; + $user->delete_after = $ts; + $profile->delete_after = $ts; + $user->save(); + $profile->save(); - if($action == 'mark-spammer') { - AccountInterstitial::whereType('post.autospam') - ->whereItemType('App\Status') - ->whereNull('appeal_handled_at') - ->whereUserId($appeal->user_id) - ->update(['appeal_handled_at' => $now, 'is_spam' => true]); + ModLogService::boot() + ->objectUid($user->id) + ->objectId($user->id) + ->objectType('App\User::class') + ->user($request->user()) + ->action('admin.user.delete') + ->accessLevel('admin') + ->save(); - $pro = Profile::whereUserId($appeal->user_id)->firstOrFail(); + Cache::forget('profiles:private'); + DeleteAccountPipeline::dispatch($user); - $pro->update([ - 'unlisted' => true, - 'cw' => true, - 'no_autolink' => true - ]); + return; + } - Status::whereProfileId($pro->id) - ->get() - ->each(function($report) { - $status->is_nsfw = $meta->is_nsfw; - $status->scope = 'public'; - $status->visibility = 'public'; - $status->save(); - StatusService::del($status->id, true); - }); + 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; - } + 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'); - $status = $appeal->status; - $status->is_nsfw = $meta->is_nsfw; - $status->scope = 'public'; - $status->visibility = 'public'; - $status->save(); + return $res; + } - $appeal->is_spam = false; - $appeal->appeal_handled_at = now(); - $appeal->save(); + 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'); - StatusService::del($status->id); + return $res; + } - 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'); + 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, 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; - } + return $res; + } - public function updateAppeal(Request $request, $id) - { - $this->validate($request, [ - 'action' => 'required|in:dismiss,approve' - ]); + if ($action == 'mark-spammer') { + AccountInterstitial::whereType('post.autospam') + ->whereItemType('App\Status') + ->whereNull('appeal_handled_at') + ->whereUserId($appeal->user_id) + ->update(['appeal_handled_at' => $now, 'is_spam' => true]); - $action = $request->input('action'); - $appeal = AccountInterstitial::whereNotNull('appeal_requested_at') - ->whereNull('appeal_handled_at') - ->findOrFail($id); + $pro = Profile::whereUserId($appeal->user_id)->firstOrFail(); - if($action == 'dismiss') { - $appeal->appeal_handled_at = now(); - $appeal->save(); - Cache::forget('admin-dash:reports:ai-count'); - return redirect('/i/admin/reports/appeals'); - } + $pro->update([ + 'unlisted' => true, + 'cw' => true, + 'no_autolink' => true, + ]); - switch ($appeal->type) { - case 'post.cw': - $status = $appeal->status; - $status->is_nsfw = false; - $status->save(); - break; + Status::whereProfileId($pro->id) + ->get() + ->each(function ($report) { + $status->is_nsfw = $meta->is_nsfw; + $status->scope = 'public'; + $status->visibility = 'public'; + $status->save(); + StatusService::del($status->id, true); + }); - case 'post.unlist': - $status = $appeal->status; - $status->scope = 'public'; - $status->visibility = 'public'; - $status->save(); - break; + 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'); - default: - # code... - break; - } + return $res; + } - $appeal->appeal_handled_at = now(); - $appeal->save(); - StatusService::del($status->id, true); - Cache::forget('admin-dash:reports:ai-count'); + $status = $appeal->status; + $status->is_nsfw = $meta->is_nsfw; + $status->scope = 'public'; + $status->visibility = 'public'; + $status->save(); - return redirect('/i/admin/reports/appeals'); - } + $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, true); + Cache::forget('admin-dash:reports:ai-count'); + + return redirect('/i/admin/reports/appeals'); + } public function updateReport(Request $request, $id) { $this->validate($request, [ - 'action' => 'required|string', + 'action' => 'required|string', ]); $action = $request->input('action'); @@ -470,7 +477,7 @@ trait AdminReportController 'ban', ]; - if (!in_array($action, $actions)) { + if (! in_array($action, $actions)) { return abort(403); } @@ -479,7 +486,7 @@ trait AdminReportController $this->handleReportAction($report, $action); Cache::forget('admin-dash:reports:list-cache'); - return response()->json(['msg'=> 'Success']); + return response()->json(['msg' => 'Success']); } public function handleReportAction(Report $report, $action) @@ -541,7 +548,7 @@ trait AdminReportController '3' => 'unlist', '4' => 'delete', '5' => 'shadowban', - '6' => 'ban' + '6' => 'ban', ]; } @@ -549,675 +556,775 @@ trait AdminReportController { $this->validate($request, [ 'action' => 'required|integer|min:1|max:10', - 'ids' => 'required|array' + 'ids' => 'required|array', ]); $action = $this->actionMap()[$request->input('action')]; $ids = $request->input('ids'); $reports = Report::whereIn('id', $ids)->whereNull('admin_seen')->get(); - foreach($reports as $report) { + foreach ($reports as $report) { $this->handleReportAction($report, $action); } $res = [ 'message' => 'Success', - 'code' => 200 + 'code' => 200, ]; + return response()->json($res); } public function reportMailVerifications(Request $request) { - $ids = Redis::smembers('email:manual'); - $ignored = Redis::smembers('email:manual-ignored'); - $reports = []; - if($ids) { - $reports = collect($ids) - ->filter(function($id) use($ignored) { - return !in_array($id, $ignored); - }) - ->map(function($id) { - $user = User::whereProfileId($id)->first(); - if(!$user || $user->email_verified_at) { - return []; - } - $account = AccountService::get($id, true); - if(!$account) { - return []; - } - $account['email'] = $user->email; - return $account; - }) - ->filter(function($res) { - return $res && isset($res['id']); - }) - ->values(); - } - return view('admin.reports.mail_verification', compact('reports', 'ignored')); + $ids = Redis::smembers('email:manual'); + $ignored = Redis::smembers('email:manual-ignored'); + $reports = []; + if ($ids) { + $reports = collect($ids) + ->filter(function ($id) use ($ignored) { + return ! in_array($id, $ignored); + }) + ->map(function ($id) { + $user = User::whereProfileId($id)->first(); + if (! $user || $user->email_verified_at) { + return []; + } + $account = AccountService::get($id, true); + if (! $account) { + return []; + } + $account['email'] = $user->email; + + return $account; + }) + ->filter(function ($res) { + return $res && isset($res['id']); + }) + ->values(); + } + + return view('admin.reports.mail_verification', compact('reports', 'ignored')); } public function reportMailVerifyIgnore(Request $request) { - $id = $request->input('id'); - Redis::sadd('email:manual-ignored', $id); - return redirect('/i/admin/reports'); + $id = $request->input('id'); + Redis::sadd('email:manual-ignored', $id); + + return redirect('/i/admin/reports'); } public function reportMailVerifyApprove(Request $request) { - $id = $request->input('id'); - $user = User::whereProfileId($id)->firstOrFail(); - Redis::srem('email:manual', $id); - Redis::srem('email:manual-ignored', $id); - $user->email_verified_at = now(); - $user->save(); - return redirect('/i/admin/reports'); + $id = $request->input('id'); + $user = User::whereProfileId($id)->firstOrFail(); + Redis::srem('email:manual', $id); + Redis::srem('email:manual-ignored', $id); + $user->email_verified_at = now(); + $user->save(); + + return redirect('/i/admin/reports'); } public function reportMailVerifyClearIgnored(Request $request) { - Redis::del('email:manual-ignored'); - return [200]; + Redis::del('email:manual-ignored'); + + return [200]; } public function reportsStats(Request $request) { - $stats = [ - 'total' => Report::count(), - 'open' => Report::whereNull('admin_seen')->count(), - 'closed' => Report::whereNotNull('admin_seen')->count(), - 'autospam' => AccountInterstitial::whereType('post.autospam')->count(), - 'autospam_open' => AccountInterstitial::whereType('post.autospam')->whereNull(['appeal_handled_at'])->count(), - 'appeals' => AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count(), - 'email_verification_requests' => Redis::scard('email:manual') - ]; - return $stats; + $stats = [ + 'total' => Report::count(), + 'open' => Report::whereNull('admin_seen')->count(), + 'closed' => Report::whereNotNull('admin_seen')->count(), + 'autospam' => AccountInterstitial::whereType('post.autospam')->count(), + 'autospam_open' => AccountInterstitial::whereType('post.autospam')->whereNull(['appeal_handled_at'])->count(), + 'appeals' => AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count(), + 'email_verification_requests' => Redis::scard('email:manual'), + ]; + + return $stats; } public function reportsApiAll(Request $request) { - $filter = $request->input('filter') == 'closed' ? 'closed' : 'open'; + $filter = $request->input('filter') == 'closed' ? 'closed' : 'open'; - $reports = AdminReport::collection( - Report::orderBy('id','desc') - ->when($filter, function($q, $filter) { - return $filter == 'open' ? - $q->whereNull('admin_seen') : - $q->whereNotNull('admin_seen'); - }) - ->groupBy(['id', 'object_id', 'object_type', 'profile_id']) - ->cursorPaginate(6) - ->withQueryString() - ); + $reports = AdminReport::collection( + Report::orderBy('id', 'desc') + ->when($filter, function ($q, $filter) { + return $filter == 'open' ? + $q->whereNull('admin_seen') : + $q->whereNotNull('admin_seen'); + }) + ->groupBy(['id', 'object_id', 'object_type', 'profile_id']) + ->cursorPaginate(6) + ->withQueryString() + ); - return $reports; + return $reports; } public function reportsApiGet(Request $request, $id) { - $report = Report::findOrFail($id); - return new AdminReport($report); + $report = Report::findOrFail($id); + + return new AdminReport($report); } public function reportsApiHandle(Request $request) { - $this->validate($request, [ - 'object_id' => 'required', - 'object_type' => 'required', - 'id' => 'required', - 'action' => 'required|in:ignore,nsfw,unlist,private,delete', - 'action_type' => 'required|in:post,profile' - ]); + $this->validate($request, [ + 'object_id' => 'required', + 'object_type' => 'required', + 'id' => 'required', + 'action' => 'required|in:ignore,nsfw,unlist,private,delete,delete-all', + 'action_type' => 'required|in:post,profile,story', + ]); - $report = Report::whereObjectId($request->input('object_id'))->findOrFail($request->input('id')); + $report = Report::whereObjectId($request->input('object_id'))->findOrFail($request->input('id')); - if($request->input('action_type') === 'profile') { - return $this->reportsHandleProfileAction($report, $request->input('action')); - } else if($request->input('action_type') === 'post') { - return $this->reportsHandleStatusAction($report, $request->input('action')); - } + if ($request->input('action_type') === 'profile') { + return $this->reportsHandleProfileAction($report, $request->input('action')); + } elseif ($request->input('action_type') === 'post') { + return $this->reportsHandleStatusAction($report, $request->input('action')); + } elseif ($request->input('action_type') === 'story') { + return $this->reportsHandleStoryAction($report, $request->input('action')); + } - return $report; + return $report; + } + + protected function reportsHandleStoryAction($report, $action) + { + switch ($action) { + case 'ignore': + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); + + return [200]; + break; + + case 'delete': + $profile = Profile::find($report->reported_profile_id); + $story = Story::whereProfileId($profile->id)->find($report->object_id); + + abort_if(! $story, 400, 'Invalid or missing story'); + + $story->active = false; + $story->save(); + + ModLogService::boot() + ->objectUid($profile->id) + ->objectId($report->object_id) + ->objectType('App\Story::class') + ->user(request()->user()) + ->action('admin.user.moderate') + ->metadata([ + 'action' => 'delete', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); + + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); + StoryDelete::dispatch($story)->onQueue('story'); + + return [200]; + break; + + case 'delete-all': + $profile = Profile::find($report->reported_profile_id); + $stories = Story::whereProfileId($profile->id)->whereActive(true)->get(); + + abort_if(! $stories || ! $stories->count(), 400, 'Invalid or missing stories'); + + ModLogService::boot() + ->objectUid($profile->id) + ->objectId($report->object_id) + ->objectType('App\Story::class') + ->user(request()->user()) + ->action('admin.user.moderate') + ->metadata([ + 'action' => 'delete-all', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); + + Report::where('reported_profile_id', $profile->id) + ->whereObjectType('App\Story') + ->whereNull('admin_seen') + ->update([ + 'admin_seen' => now(), + ]); + $stories->each(function ($story) { + StoryDelete::dispatch($story)->onQueue('story'); + }); + + return [200]; + break; + } } protected function reportsHandleProfileAction($report, $action) { - switch($action) { - case 'ignore': - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); - return [200]; - break; + switch ($action) { + case 'ignore': + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); - case 'nsfw': - if($report->object_type === 'App\Profile') { - $profile = Profile::find($report->object_id); - } else if($report->object_type === 'App\Status') { - $status = Status::find($report->object_id); - if(!$status) { - return [200]; - } - $profile = Profile::find($status->profile_id); - } + return [200]; + break; - if(!$profile) { - return; - } + case 'nsfw': + if ($report->object_type === 'App\Profile') { + $profile = Profile::find($report->object_id); + } elseif ($report->object_type === 'App\Status') { + $status = Status::find($report->object_id); + if (! $status) { + return [200]; + } + $profile = Profile::find($status->profile_id); + } - abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.'); + if (! $profile) { + return; + } - $profile->cw = true; - $profile->save(); + abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.'); - foreach(Status::whereProfileId($profile->id)->cursor() as $status) { - $status->is_nsfw = true; - $status->save(); - StatusService::del($status->id); - PublicTimelineService::rem($status->id); - } + $profile->cw = true; + $profile->save(); - ModLogService::boot() - ->objectUid($profile->id) - ->objectId($profile->id) - ->objectType('App\Profile::class') - ->user(request()->user()) - ->action('admin.user.moderate') - ->metadata([ - 'action' => 'cw', - 'message' => 'Success!' - ]) - ->accessLevel('admin') - ->save(); + foreach (Status::whereProfileId($profile->id)->cursor() as $status) { + $status->is_nsfw = true; + $status->save(); + StatusService::del($status->id); + PublicTimelineService::rem($status->id); + } - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'nsfw' => true, - 'admin_seen' => now() - ]); - return [200]; - break; + ModLogService::boot() + ->objectUid($profile->id) + ->objectId($profile->id) + ->objectType('App\Profile::class') + ->user(request()->user()) + ->action('admin.user.moderate') + ->metadata([ + 'action' => 'cw', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); - case 'unlist': - if($report->object_type === 'App\Profile') { - $profile = Profile::find($report->object_id); - } else if($report->object_type === 'App\Status') { - $status = Status::find($report->object_id); - if(!$status) { - return [200]; - } - $profile = Profile::find($status->profile_id); - } + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'nsfw' => true, + 'admin_seen' => now(), + ]); - if(!$profile) { - return; - } + return [200]; + break; - abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.'); + case 'unlist': + if ($report->object_type === 'App\Profile') { + $profile = Profile::find($report->object_id); + } elseif ($report->object_type === 'App\Status') { + $status = Status::find($report->object_id); + if (! $status) { + return [200]; + } + $profile = Profile::find($status->profile_id); + } - $profile->unlisted = true; - $profile->save(); + if (! $profile) { + return; + } - foreach(Status::whereProfileId($profile->id)->whereScope('public')->cursor() as $status) { - $status->scope = 'unlisted'; - $status->visibility = 'unlisted'; - $status->save(); - StatusService::del($status->id); - PublicTimelineService::rem($status->id); - } + abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.'); - ModLogService::boot() - ->objectUid($profile->id) - ->objectId($profile->id) - ->objectType('App\Profile::class') - ->user(request()->user()) - ->action('admin.user.moderate') - ->metadata([ - 'action' => 'unlisted', - 'message' => 'Success!' - ]) - ->accessLevel('admin') - ->save(); + $profile->unlisted = true; + $profile->save(); - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); - return [200]; - break; + foreach (Status::whereProfileId($profile->id)->whereScope('public')->cursor() as $status) { + $status->scope = 'unlisted'; + $status->visibility = 'unlisted'; + $status->save(); + StatusService::del($status->id); + PublicTimelineService::rem($status->id); + } - case 'private': - if($report->object_type === 'App\Profile') { - $profile = Profile::find($report->object_id); - } else if($report->object_type === 'App\Status') { - $status = Status::find($report->object_id); - if(!$status) { - return [200]; - } - $profile = Profile::find($status->profile_id); - } + ModLogService::boot() + ->objectUid($profile->id) + ->objectId($profile->id) + ->objectType('App\Profile::class') + ->user(request()->user()) + ->action('admin.user.moderate') + ->metadata([ + 'action' => 'unlisted', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); - if(!$profile) { - return; - } + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); - abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.'); + return [200]; + break; - $profile->unlisted = true; - $profile->save(); + case 'private': + if ($report->object_type === 'App\Profile') { + $profile = Profile::find($report->object_id); + } elseif ($report->object_type === 'App\Status') { + $status = Status::find($report->object_id); + if (! $status) { + return [200]; + } + $profile = Profile::find($status->profile_id); + } - foreach(Status::whereProfileId($profile->id)->cursor() as $status) { - $status->scope = 'private'; - $status->visibility = 'private'; - $status->save(); - StatusService::del($status->id); - PublicTimelineService::rem($status->id); - } + if (! $profile) { + return; + } - ModLogService::boot() - ->objectUid($profile->id) - ->objectId($profile->id) - ->objectType('App\Profile::class') - ->user(request()->user()) - ->action('admin.user.moderate') - ->metadata([ - 'action' => 'private', - 'message' => 'Success!' - ]) - ->accessLevel('admin') - ->save(); + abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.'); - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); - return [200]; - break; + $profile->unlisted = true; + $profile->save(); - case 'delete': - if(config('pixelfed.account_deletion') == false) { - abort(404); - } + foreach (Status::whereProfileId($profile->id)->cursor() as $status) { + $status->scope = 'private'; + $status->visibility = 'private'; + $status->save(); + StatusService::del($status->id); + PublicTimelineService::rem($status->id); + } - if($report->object_type === 'App\Profile') { - $profile = Profile::find($report->object_id); - } else if($report->object_type === 'App\Status') { - $status = Status::find($report->object_id); - if(!$status) { - return [200]; - } - $profile = Profile::find($status->profile_id); - } + ModLogService::boot() + ->objectUid($profile->id) + ->objectId($profile->id) + ->objectType('App\Profile::class') + ->user(request()->user()) + ->action('admin.user.moderate') + ->metadata([ + 'action' => 'private', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); - if(!$profile) { - return; - } + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); - abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot delete an admin account.'); + return [200]; + break; - $ts = now()->addMonth(); + case 'delete': + if (config('pixelfed.account_deletion') == false) { + abort(404); + } - if($profile->user_id) { - $user = $profile->user; - abort_if($user->is_admin, 403, 'You cannot delete admin accounts.'); - $user->status = 'delete'; - $user->delete_after = $ts; - $user->save(); - } + if ($report->object_type === 'App\Profile') { + $profile = Profile::find($report->object_id); + } elseif ($report->object_type === 'App\Status') { + $status = Status::find($report->object_id); + if (! $status) { + return [200]; + } + $profile = Profile::find($status->profile_id); + } - $profile->status = 'delete'; - $profile->delete_after = $ts; - $profile->save(); + if (! $profile) { + return; + } - ModLogService::boot() - ->objectUid($profile->id) - ->objectId($profile->id) - ->objectType('App\Profile::class') - ->user(request()->user()) - ->action('admin.user.delete') - ->accessLevel('admin') - ->save(); + abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot delete an admin account.'); - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); + $ts = now()->addMonth(); - if($profile->user_id) { - DB::table('oauth_access_tokens')->whereUserId($user->id)->delete(); - DB::table('oauth_auth_codes')->whereUserId($user->id)->delete(); - $user->email = $user->id; - $user->password = ''; - $user->status = 'delete'; - $user->save(); - $profile->status = 'delete'; - $profile->delete_after = now()->addMonth(); - $profile->save(); - AccountService::del($profile->id); - DeleteAccountPipeline::dispatch($user)->onQueue('high'); - } else { - $profile->status = 'delete'; - $profile->delete_after = now()->addMonth(); - $profile->save(); - AccountService::del($profile->id); - DeleteRemoteProfilePipeline::dispatch($profile)->onQueue('high'); - } - return [200]; - break; - } + if ($profile->user_id) { + $user = $profile->user; + abort_if($user->is_admin, 403, 'You cannot delete admin accounts.'); + $user->status = 'delete'; + $user->delete_after = $ts; + $user->save(); + } + + $profile->status = 'delete'; + $profile->delete_after = $ts; + $profile->save(); + + ModLogService::boot() + ->objectUid($profile->id) + ->objectId($profile->id) + ->objectType('App\Profile::class') + ->user(request()->user()) + ->action('admin.user.delete') + ->accessLevel('admin') + ->save(); + + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); + + if ($profile->user_id) { + DB::table('oauth_access_tokens')->whereUserId($user->id)->delete(); + DB::table('oauth_auth_codes')->whereUserId($user->id)->delete(); + $user->email = $user->id; + $user->password = ''; + $user->status = 'delete'; + $user->save(); + $profile->status = 'delete'; + $profile->delete_after = now()->addMonth(); + $profile->save(); + AccountService::del($profile->id); + DeleteAccountPipeline::dispatch($user)->onQueue('high'); + } else { + $profile->status = 'delete'; + $profile->delete_after = now()->addMonth(); + $profile->save(); + AccountService::del($profile->id); + DeleteRemoteProfilePipeline::dispatch($profile)->onQueue('high'); + } + + return [200]; + break; + } } protected function reportsHandleStatusAction($report, $action) { - switch($action) { - case 'ignore': - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); - return [200]; - break; + switch ($action) { + case 'ignore': + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); - case 'nsfw': - $status = Status::find($report->object_id); + return [200]; + break; - if(!$status) { - return [200]; - } + case 'nsfw': + $status = Status::find($report->object_id); - abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.'); - $status->is_nsfw = true; - $status->save(); - StatusService::del($status->id); + if (! $status) { + return [200]; + } - ModLogService::boot() - ->objectUid($status->profile_id) - ->objectId($status->profile_id) - ->objectType('App\Status::class') - ->user(request()->user()) - ->action('admin.status.moderate') - ->metadata([ - 'action' => 'cw', - 'message' => 'Success!' - ]) - ->accessLevel('admin') - ->save(); + abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.'); + $status->is_nsfw = true; + $status->save(); + StatusService::del($status->id); - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'nsfw' => true, - 'admin_seen' => now() - ]); - return [200]; - break; + ModLogService::boot() + ->objectUid($status->profile_id) + ->objectId($status->profile_id) + ->objectType('App\Status::class') + ->user(request()->user()) + ->action('admin.status.moderate') + ->metadata([ + 'action' => 'cw', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); - case 'private': - $status = Status::find($report->object_id); + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'nsfw' => true, + 'admin_seen' => now(), + ]); - if(!$status) { - return [200]; - } + return [200]; + break; - abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.'); + case 'private': + $status = Status::find($report->object_id); - $status->scope = 'private'; - $status->visibility = 'private'; - $status->save(); - StatusService::del($status->id); - PublicTimelineService::rem($status->id); + if (! $status) { + return [200]; + } - ModLogService::boot() - ->objectUid($status->profile_id) - ->objectId($status->profile_id) - ->objectType('App\Status::class') - ->user(request()->user()) - ->action('admin.status.moderate') - ->metadata([ - 'action' => 'private', - 'message' => 'Success!' - ]) - ->accessLevel('admin') - ->save(); + abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.'); - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); - return [200]; - break; + $status->scope = 'private'; + $status->visibility = 'private'; + $status->save(); + StatusService::del($status->id); + PublicTimelineService::rem($status->id); - case 'unlist': - $status = Status::find($report->object_id); + ModLogService::boot() + ->objectUid($status->profile_id) + ->objectId($status->profile_id) + ->objectType('App\Status::class') + ->user(request()->user()) + ->action('admin.status.moderate') + ->metadata([ + 'action' => 'private', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); - if(!$status) { - return [200]; - } + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); - abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.'); + return [200]; + break; - if($status->scope === 'public') { - $status->scope = 'unlisted'; - $status->visibility = 'unlisted'; - $status->save(); - StatusService::del($status->id); - PublicTimelineService::rem($status->id); - } + case 'unlist': + $status = Status::find($report->object_id); - ModLogService::boot() - ->objectUid($status->profile_id) - ->objectId($status->profile_id) - ->objectType('App\Status::class') - ->user(request()->user()) - ->action('admin.status.moderate') - ->metadata([ - 'action' => 'unlist', - 'message' => 'Success!' - ]) - ->accessLevel('admin') - ->save(); + if (! $status) { + return [200]; + } - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); - return [200]; - break; + abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.'); - case 'delete': - $status = Status::find($report->object_id); + if ($status->scope === 'public') { + $status->scope = 'unlisted'; + $status->visibility = 'unlisted'; + $status->save(); + StatusService::del($status->id); + PublicTimelineService::rem($status->id); + } - if(!$status) { - return [200]; - } + ModLogService::boot() + ->objectUid($status->profile_id) + ->objectId($status->profile_id) + ->objectType('App\Status::class') + ->user(request()->user()) + ->action('admin.status.moderate') + ->metadata([ + 'action' => 'unlist', + 'message' => 'Success!', + ]) + ->accessLevel('admin') + ->save(); - $profile = $status->profile; + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); - abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot delete an admin account post.'); + return [200]; + break; - StatusService::del($status->id); + case 'delete': + $status = Status::find($report->object_id); - if($profile->user_id != null && $profile->domain == null) { - PublicTimelineService::del($status->id); - StatusDelete::dispatch($status)->onQueue('high'); - } else { - NetworkTimelineService::del($status->id); - RemoteStatusDelete::dispatch($status)->onQueue('high'); - } + if (! $status) { + return [200]; + } - Report::whereObjectId($report->object_id) - ->whereObjectType($report->object_type) - ->update([ - 'admin_seen' => now() - ]); + $profile = $status->profile; - return [200]; - break; - } + abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot delete an admin account post.'); + + StatusService::del($status->id); + + if ($profile->user_id != null && $profile->domain == null) { + PublicTimelineService::del($status->id); + StatusDelete::dispatch($status)->onQueue('high'); + } else { + NetworkTimelineService::del($status->id); + RemoteStatusDelete::dispatch($status)->onQueue('high'); + } + + Report::whereObjectId($report->object_id) + ->whereObjectType($report->object_type) + ->update([ + 'admin_seen' => now(), + ]); + + return [200]; + break; + } } public function reportsApiSpamAll(Request $request) { - $tab = $request->input('tab', 'home'); + $tab = $request->input('tab', 'home'); - $appeals = AdminSpamReport::collection( - AccountInterstitial::orderBy('id', 'desc') - ->whereType('post.autospam') - ->whereNull('appeal_handled_at') - ->cursorPaginate(6) - ->withQueryString() - ); + $appeals = AdminSpamReport::collection( + AccountInterstitial::orderBy('id', 'desc') + ->whereType('post.autospam') + ->whereNull('appeal_handled_at') + ->cursorPaginate(6) + ->withQueryString() + ); - return $appeals; + return $appeals; } public function reportsApiSpamHandle(Request $request) { - $this->validate($request, [ - 'id' => 'required', - 'action' => 'required|in:mark-read,mark-not-spam,mark-all-read,mark-all-not-spam,delete-profile', - ]); + $this->validate($request, [ + 'id' => 'required', + 'action' => 'required|in:mark-read,mark-not-spam,mark-all-read,mark-all-not-spam,delete-profile', + ]); - $action = $request->input('action'); + $action = $request->input('action'); - abort_if( - $action === 'delete-profile' && - !config('pixelfed.account_deletion'), - 404, - "Cannot delete profile, account_deletion is disabled.\n\n Set `ACCOUNT_DELETION=true` in .env and re-cache config." - ); + abort_if( + $action === 'delete-profile' && + ! config('pixelfed.account_deletion'), + 404, + "Cannot delete profile, account_deletion is disabled.\n\n Set `ACCOUNT_DELETION=true` in .env and re-cache config." + ); - $report = AccountInterstitial::with('user') - ->whereType('post.autospam') - ->whereNull('appeal_handled_at') - ->findOrFail($request->input('id')); + $report = AccountInterstitial::with('user') + ->whereType('post.autospam') + ->whereNull('appeal_handled_at') + ->findOrFail($request->input('id')); - $this->reportsHandleSpamAction($report, $action); - Cache::forget('admin-dash:reports:spam-count'); - Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $report->user->profile_id); - Cache::forget('pf:bouncer_v0:recent_by_pid:' . $report->user->profile_id); - return [$action, $report]; + $this->reportsHandleSpamAction($report, $action); + Cache::forget('admin-dash:reports:spam-count'); + Cache::forget('pf:bouncer_v0:exemption_by_pid:'.$report->user->profile_id); + Cache::forget('pf:bouncer_v0:recent_by_pid:'.$report->user->profile_id); + + return [$action, $report]; } public function reportsHandleSpamAction($appeal, $action) { - $meta = json_decode($appeal->meta); + $meta = json_decode($appeal->meta); - if($action == 'mark-read') { - $appeal->is_spam = true; - $appeal->appeal_handled_at = now(); - $appeal->save(); - PublicTimelineService::del($appeal->item_id); - } + if ($action == 'mark-read') { + $appeal->is_spam = true; + $appeal->appeal_handled_at = now(); + $appeal->save(); + PublicTimelineService::del($appeal->item_id); + } - if($action == 'mark-not-spam') { - $status = $appeal->status; - $status->is_nsfw = $meta->is_nsfw; - $status->scope = 'public'; - $status->visibility = 'public'; - $status->save(); + if ($action == 'mark-not-spam') { + $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(); + $appeal->is_spam = false; + $appeal->appeal_handled_at = now(); + $appeal->save(); - Notification::whereAction('autospam.warning') - ->whereProfileId($appeal->user->profile_id) - ->get() - ->each(function($n) use($appeal) { - NotificationService::del($appeal->user->profile_id, $n->id); - $n->forceDelete(); - }); + Notification::whereAction('autospam.warning') + ->whereProfileId($appeal->user->profile_id) + ->get() + ->each(function ($n) use ($appeal) { + NotificationService::del($appeal->user->profile_id, $n->id); + $n->forceDelete(); + }); - StatusService::del($status->id); - StatusService::get($status->id); - if($status->in_reply_to_id == null && $status->reblog_of_id == null) { - PublicTimelineService::add($status->id); - } - } + StatusService::del($status->id); + StatusService::get($status->id); + if ($status->in_reply_to_id == null && $status->reblog_of_id == null) { + PublicTimelineService::add($status->id); + } + } - if($action == 'mark-all-read') { - AccountInterstitial::whereType('post.autospam') - ->whereItemType('App\Status') - ->whereNull('appeal_handled_at') - ->whereUserId($appeal->user_id) - ->update([ - 'appeal_handled_at' => now(), - 'is_spam' => true - ]); - } + if ($action == 'mark-all-read') { + AccountInterstitial::whereType('post.autospam') + ->whereItemType('App\Status') + ->whereNull('appeal_handled_at') + ->whereUserId($appeal->user_id) + ->update([ + 'appeal_handled_at' => now(), + 'is_spam' => true, + ]); + } - if($action == 'mark-all-not-spam') { - AccountInterstitial::whereType('post.autospam') - ->whereItemType('App\Status') - ->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); - } - Notification::whereAction('autospam.warning') - ->whereProfileId($report->user->profile_id) - ->get() - ->each(function($n) use($report) { - NotificationService::del($report->user->profile_id, $n->id); - $n->forceDelete(); - }); - }); - } + if ($action == 'mark-all-not-spam') { + AccountInterstitial::whereType('post.autospam') + ->whereItemType('App\Status') + ->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); + } + Notification::whereAction('autospam.warning') + ->whereProfileId($report->user->profile_id) + ->get() + ->each(function ($n) use ($report) { + NotificationService::del($report->user->profile_id, $n->id); + $n->forceDelete(); + }); + }); + } - if($action == 'delete-profile') { - $user = User::findOrFail($appeal->user_id); - $profile = $user->profile; + if ($action == 'delete-profile') { + $user = User::findOrFail($appeal->user_id); + $profile = $user->profile; - if($user->is_admin == true) { - $mid = request()->user()->id; - abort_if($user->id < $mid, 403, 'You cannot delete an admin account.'); - } + if ($user->is_admin == true) { + $mid = request()->user()->id; + abort_if($user->id < $mid, 403, 'You cannot delete an admin account.'); + } - $ts = now()->addMonth(); - $user->status = 'delete'; - $profile->status = 'delete'; - $user->delete_after = $ts; - $profile->delete_after = $ts; - $user->save(); - $profile->save(); + $ts = now()->addMonth(); + $user->status = 'delete'; + $profile->status = 'delete'; + $user->delete_after = $ts; + $profile->delete_after = $ts; + $user->save(); + $profile->save(); - $appeal->appeal_handled_at = now(); - $appeal->save(); + $appeal->appeal_handled_at = now(); + $appeal->save(); - ModLogService::boot() - ->objectUid($user->id) - ->objectId($user->id) - ->objectType('App\User::class') - ->user(request()->user()) - ->action('admin.user.delete') - ->accessLevel('admin') - ->save(); + ModLogService::boot() + ->objectUid($user->id) + ->objectId($user->id) + ->objectType('App\User::class') + ->user(request()->user()) + ->action('admin.user.delete') + ->accessLevel('admin') + ->save(); - Cache::forget('profiles:private'); - DeleteAccountPipeline::dispatch($user); - } + Cache::forget('profiles:private'); + DeleteAccountPipeline::dispatch($user); + } } public function reportsApiSpamGet(Request $request, $id) { - $report = AccountInterstitial::findOrFail($id); - return new AdminSpamReport($report); + $report = AccountInterstitial::findOrFail($id); + + return new AdminSpamReport($report); } } From 2f48df8ca847fd6d6a5c4673fd759c825bc9e62a Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Wed, 28 Feb 2024 21:06:21 -0700 Subject: [PATCH 3/3] Update kb, add email confirmation issues page --- .../help/email-confirmation-issues.blade.php | 16 ++++++++++++++++ routes/web.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 resources/views/site/help/email-confirmation-issues.blade.php diff --git a/resources/views/site/help/email-confirmation-issues.blade.php b/resources/views/site/help/email-confirmation-issues.blade.php new file mode 100644 index 00000000..6c1c52ca --- /dev/null +++ b/resources/views/site/help/email-confirmation-issues.blade.php @@ -0,0 +1,16 @@ +@extends('site.help.partial.template', ['breadcrumb'=>'Email Confirmation Issues']) + +@section('section') +
+

Email Confirmation Issues

+
+
+

If you have been redirected to this page, it may be due to one of the following reasons:

+ +
    +
  • The email confirmation link has already been used.
  • +
  • The email confirmation link may have expired, they are only valid for 24 hours.
  • +
  • You cannot confirm an email for another account while logged in to a different account. Try logging out, or use a different browser to open the email confirmation link.
  • +
  • The account the associated email belongs to may have been deleted, or the account may have changed the email address.
  • +
+@endsection diff --git a/routes/web.php b/routes/web.php index 6a5b878e..059651d2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -307,7 +307,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact Route::view('instance-max-users-limit', 'site.help.instance-max-users')->name('help.instance-max-users-limit'); Route::view('import', 'site.help.import')->name('help.import'); Route::view('parental-controls', 'site.help.parental-controls'); - // Route::view('email-confirmation-issues', 'site.help.email-confirmation-issues')->name('help.email-confirmation-issues'); + Route::view('email-confirmation-issues', 'site.help.email-confirmation-issues')->name('help.email-confirmation-issues'); Route::view('curated-onboarding', 'site.help.curated-onboarding')->name('help.curated-onboarding'); }); Route::get('newsroom/{year}/{month}/{slug}', 'NewsroomController@show');