2018-08-19 22:35:16 +00:00
< ? php
namespace App\Http\Controllers\Admin ;
2024-02-27 04:39:09 +00:00
use App\AccountInterstitial ;
use App\Http\Resources\AdminReport ;
use App\Http\Resources\AdminSpamReport ;
2022-04-18 06:30:06 +00:00
use App\Jobs\DeletePipeline\DeleteAccountPipeline ;
2023-04-24 07:52:59 +00:00
use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline ;
2023-08-01 10:30:50 +00:00
use App\Jobs\StatusPipeline\RemoteStatusDelete ;
2024-02-27 04:39:09 +00:00
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 ;
2023-05-13 11:44:46 +00:00
use App\Services\NotificationService ;
2023-04-24 07:52:59 +00:00
use App\Services\PublicTimelineService ;
2024-02-27 04:39:09 +00:00
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 ;
2018-08-19 22:35:16 +00:00
trait AdminReportController
{
2024-02-27 04:39:09 +00:00
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 ( $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 :: 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 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 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 );
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' ));
}
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,delete-account,mark-spammer' ,
]);
$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 == 'delete-account' ) {
if ( config ( 'pixelfed.account_deletion' ) == false ) {
abort ( 404 );
}
$user = User :: findOrFail ( $appeal -> user_id );
$profile = $user -> profile ;
if ( $user -> is_admin == true ) {
$mid = $request -> user () -> id ;
abort_if ( $user -> id < $mid , 403 );
}
$ts = now () -> addMonth ();
$user -> status = 'delete' ;
$profile -> status = 'delete' ;
$user -> delete_after = $ts ;
$profile -> delete_after = $ts ;
$user -> save ();
$profile -> 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 );
return ;
}
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 , 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 == '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 ]);
$pro = Profile :: whereUserId ( $appeal -> user_id ) -> firstOrFail ();
$pro -> update ([
'unlisted' => true ,
'cw' => true ,
'no_autolink' => true ,
]);
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 );
});
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 , true );
Cache :: forget ( 'admin-dash:reports:ai-count' );
return redirect ( '/i/admin/reports/appeals' );
}
2021-11-09 07:06:56 +00:00
2018-08-19 22:35:16 +00:00
public function updateReport ( Request $request , $id )
{
2018-08-28 03:07:36 +00:00
$this -> validate ( $request , [
2024-02-27 04:39:09 +00:00
'action' => 'required|string' ,
2018-08-28 03:07:36 +00:00
]);
2018-08-19 22:35:16 +00:00
2018-08-28 03:07:36 +00:00
$action = $request -> input ( 'action' );
2018-08-19 22:35:16 +00:00
2018-08-28 03:07:36 +00:00
$actions = [
'ignore' ,
'cw' ,
'unlist' ,
'delete' ,
'shadowban' ,
'ban' ,
];
2018-08-19 22:35:16 +00:00
2024-02-27 04:39:09 +00:00
if ( ! in_array ( $action , $actions )) {
2018-08-28 03:07:36 +00:00
return abort ( 403 );
}
2018-08-19 22:35:16 +00:00
2018-08-28 03:07:36 +00:00
$report = Report :: findOrFail ( $id );
2018-08-19 22:35:16 +00:00
2018-08-28 03:07:36 +00:00
$this -> handleReportAction ( $report , $action );
2021-11-09 06:02:34 +00:00
Cache :: forget ( 'admin-dash:reports:list-cache' );
2018-08-19 22:35:16 +00:00
2024-02-27 04:39:09 +00:00
return response () -> json ([ 'msg' => 'Success' ]);
2018-08-19 22:35:16 +00:00
}
public function handleReportAction ( Report $report , $action )
{
2018-08-28 03:07:36 +00:00
$item = $report -> reported ();
$report -> admin_seen = Carbon :: now ();
switch ( $action ) {
case 'ignore' :
$report -> not_interested = true ;
break ;
case 'cw' :
2018-12-05 03:11:50 +00:00
Cache :: forget ( 'status:thumb:' . $item -> id );
2018-08-28 03:07:36 +00:00
$item -> is_nsfw = true ;
$item -> save ();
$report -> nsfw = true ;
2021-12-13 05:30:55 +00:00
StatusService :: del ( $item -> id , true );
2018-08-28 03:07:36 +00:00
break ;
case 'unlist' :
$item -> visibility = 'unlisted' ;
$item -> save ();
2019-02-25 06:22:06 +00:00
Cache :: forget ( 'profiles:private' );
2021-12-13 05:30:55 +00:00
StatusService :: del ( $item -> id , true );
2018-08-28 03:07:36 +00:00
break ;
case 'delete' :
// Todo: fire delete job
$report -> admin_seen = null ;
2021-12-13 05:30:55 +00:00
StatusService :: del ( $item -> id , true );
2018-08-28 03:07:36 +00:00
break ;
case 'shadowban' :
// Todo: fire delete job
$report -> admin_seen = null ;
break ;
case 'ban' :
// Todo: fire delete job
$report -> admin_seen = null ;
break ;
default :
$report -> admin_seen = null ;
break ;
}
$report -> save ();
return $this ;
2018-08-19 22:35:16 +00:00
}
2018-09-02 03:35:32 +00:00
protected function actionMap ()
{
return [
'1' => 'ignore' ,
'2' => 'cw' ,
'3' => 'unlist' ,
'4' => 'delete' ,
'5' => 'shadowban' ,
2024-02-27 04:39:09 +00:00
'6' => 'ban' ,
2018-09-02 03:35:32 +00:00
];
}
public function bulkUpdateReport ( Request $request )
{
$this -> validate ( $request , [
'action' => 'required|integer|min:1|max:10' ,
2024-02-27 04:39:09 +00:00
'ids' => 'required|array' ,
2018-09-02 03:35:32 +00:00
]);
$action = $this -> actionMap ()[ $request -> input ( 'action' )];
$ids = $request -> input ( 'ids' );
$reports = Report :: whereIn ( 'id' , $ids ) -> whereNull ( 'admin_seen' ) -> get ();
2024-02-27 04:39:09 +00:00
foreach ( $reports as $report ) {
2018-09-02 03:35:32 +00:00
$this -> handleReportAction ( $report , $action );
}
$res = [
'message' => 'Success' ,
2024-02-27 04:39:09 +00:00
'code' => 200 ,
2018-09-02 03:35:32 +00:00
];
2024-02-27 04:39:09 +00:00
2018-09-02 03:35:32 +00:00
return response () -> json ( $res );
}
2021-11-09 06:02:34 +00:00
public function reportMailVerifications ( Request $request )
{
2024-02-27 04:39:09 +00:00
$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' ));
2021-11-09 06:02:34 +00:00
}
public function reportMailVerifyIgnore ( Request $request )
{
2024-02-27 04:39:09 +00:00
$id = $request -> input ( 'id' );
Redis :: sadd ( 'email:manual-ignored' , $id );
return redirect ( '/i/admin/reports' );
2021-11-09 06:02:34 +00:00
}
public function reportMailVerifyApprove ( Request $request )
{
2024-02-27 04:39:09 +00:00
$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' );
2021-11-09 06:02:34 +00:00
}
public function reportMailVerifyClearIgnored ( Request $request )
{
2024-02-27 04:39:09 +00:00
Redis :: del ( 'email:manual-ignored' );
return [ 200 ];
2021-11-09 06:02:34 +00:00
}
2023-04-24 07:52:59 +00:00
public function reportsStats ( Request $request )
{
2024-02-27 04:39:09 +00:00
$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 ;
2023-04-24 07:52:59 +00:00
}
public function reportsApiAll ( Request $request )
{
2024-02-27 04:39:09 +00:00
$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 ()
);
return $reports ;
2023-04-24 07:52:59 +00:00
}
public function reportsApiGet ( Request $request , $id )
{
2024-02-27 04:39:09 +00:00
$report = Report :: findOrFail ( $id );
return new AdminReport ( $report );
2023-04-24 07:52:59 +00:00
}
public function reportsApiHandle ( Request $request )
{
2024-02-27 04:39:09 +00:00
$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' ));
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 ;
}
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 ;
}
2023-04-24 07:52:59 +00:00
}
protected function reportsHandleProfileAction ( $report , $action )
{
2024-02-27 04:39:09 +00:00
switch ( $action ) {
case 'ignore' :
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'admin_seen' => now (),
]);
return [ 200 ];
break ;
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 );
}
if ( ! $profile ) {
return ;
}
abort_if ( $profile -> user && $profile -> user -> is_admin , 400 , 'Cannot moderate an admin account.' );
$profile -> cw = true ;
$profile -> save ();
foreach ( Status :: whereProfileId ( $profile -> id ) -> cursor () as $status ) {
$status -> is_nsfw = true ;
$status -> save ();
StatusService :: del ( $status -> id );
PublicTimelineService :: rem ( $status -> id );
}
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 ();
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'nsfw' => true ,
'admin_seen' => now (),
]);
return [ 200 ];
break ;
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 );
}
if ( ! $profile ) {
return ;
}
abort_if ( $profile -> user && $profile -> user -> is_admin , 400 , 'Cannot moderate an admin account.' );
$profile -> unlisted = true ;
$profile -> save ();
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 );
}
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 ();
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'admin_seen' => now (),
]);
return [ 200 ];
break ;
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 );
}
if ( ! $profile ) {
return ;
}
abort_if ( $profile -> user && $profile -> user -> is_admin , 400 , 'Cannot moderate an admin account.' );
$profile -> unlisted = true ;
$profile -> save ();
foreach ( Status :: whereProfileId ( $profile -> id ) -> cursor () as $status ) {
$status -> scope = 'private' ;
$status -> visibility = 'private' ;
$status -> save ();
StatusService :: del ( $status -> id );
PublicTimelineService :: rem ( $status -> 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 ();
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'admin_seen' => now (),
]);
return [ 200 ];
break ;
case 'delete' :
if ( config ( 'pixelfed.account_deletion' ) == false ) {
abort ( 404 );
}
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 );
}
if ( ! $profile ) {
return ;
}
abort_if ( $profile -> user && $profile -> user -> is_admin , 400 , 'Cannot delete an admin account.' );
$ts = now () -> addMonth ();
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 ;
}
2023-04-24 07:52:59 +00:00
}
protected function reportsHandleStatusAction ( $report , $action )
{
2024-02-27 04:39:09 +00:00
switch ( $action ) {
case 'ignore' :
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'admin_seen' => now (),
]);
return [ 200 ];
break ;
case 'nsfw' :
$status = Status :: find ( $report -> object_id );
if ( ! $status ) {
return [ 200 ];
}
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 );
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 ();
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'nsfw' => true ,
'admin_seen' => now (),
]);
return [ 200 ];
break ;
case 'private' :
$status = Status :: find ( $report -> object_id );
if ( ! $status ) {
return [ 200 ];
}
abort_if ( $status -> profile -> user && $status -> profile -> user -> is_admin , 400 , 'Cannot moderate an admin account post.' );
$status -> scope = 'private' ;
$status -> visibility = 'private' ;
$status -> save ();
StatusService :: del ( $status -> id );
PublicTimelineService :: rem ( $status -> 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 ();
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'admin_seen' => now (),
]);
return [ 200 ];
break ;
case 'unlist' :
$status = Status :: find ( $report -> object_id );
if ( ! $status ) {
return [ 200 ];
}
abort_if ( $status -> profile -> user && $status -> profile -> user -> is_admin , 400 , 'Cannot moderate an admin account post.' );
if ( $status -> scope === 'public' ) {
$status -> scope = 'unlisted' ;
$status -> visibility = 'unlisted' ;
$status -> save ();
StatusService :: del ( $status -> id );
PublicTimelineService :: rem ( $status -> 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 ();
Report :: whereObjectId ( $report -> object_id )
-> whereObjectType ( $report -> object_type )
-> update ([
'admin_seen' => now (),
]);
return [ 200 ];
break ;
case 'delete' :
$status = Status :: find ( $report -> object_id );
if ( ! $status ) {
return [ 200 ];
}
$profile = $status -> profile ;
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 ;
}
2023-04-24 07:52:59 +00:00
}
public function reportsApiSpamAll ( Request $request )
{
2024-02-27 04:39:09 +00:00
$tab = $request -> input ( 'tab' , 'home' );
2023-04-24 07:52:59 +00:00
2024-02-27 04:39:09 +00:00
$appeals = AdminSpamReport :: collection (
AccountInterstitial :: orderBy ( 'id' , 'desc' )
-> whereType ( 'post.autospam' )
-> whereNull ( 'appeal_handled_at' )
-> cursorPaginate ( 6 )
-> withQueryString ()
);
2023-04-24 07:52:59 +00:00
2024-02-27 04:39:09 +00:00
return $appeals ;
2023-04-24 07:52:59 +00:00
}
public function reportsApiSpamHandle ( Request $request )
{
2024-02-27 04:39:09 +00:00
$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' );
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' ));
$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 ];
2023-04-24 07:52:59 +00:00
}
public function reportsHandleSpamAction ( $appeal , $action )
{
2024-02-27 04:39:09 +00:00
$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-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 ();
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 );
}
}
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 == '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.' );
}
$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 ();
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 );
}
2023-04-24 07:52:59 +00:00
}
public function reportsApiSpamGet ( Request $request , $id )
{
2024-02-27 04:39:09 +00:00
$report = AccountInterstitial :: findOrFail ( $id );
return new AdminSpamReport ( $report );
2023-04-24 07:52:59 +00:00
}
2018-08-28 03:07:36 +00:00
}