diff --git a/app/Http/Controllers/InternalApiController.php b/app/Http/Controllers/InternalApiController.php
index 90c195a28..ad34f92ae 100644
--- a/app/Http/Controllers/InternalApiController.php
+++ b/app/Http/Controllers/InternalApiController.php
@@ -28,6 +28,7 @@ use App\Transformer\Api\{
};
use App\Util\Media\Filter;
use App\Jobs\StatusPipeline\NewStatusPipeline;
+use App\Jobs\ModPipeline\HandleSpammerPipeline;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use Illuminate\Validation\Rule;
@@ -175,7 +176,8 @@ class InternalApiController extends Controller
Rule::in([
'addcw',
'remcw',
- 'unlist'
+ 'unlist',
+ 'spammer'
])
],
'item_id' => 'required|integer|min:1',
@@ -310,6 +312,23 @@ class InternalApiController extends Controller
$u->save();
}
break;
+
+ case 'spammer':
+ $status = Status::findOrFail($item_id);
+ HandleSpammerPipeline::dispatch($status->profile);
+ ModLogService::boot()
+ ->user(Auth::user())
+ ->objectUid($status->profile->user_id)
+ ->objectId($status->id)
+ ->objectType('App\User::class')
+ ->action('admin.status.moderate')
+ ->metadata([
+ 'action' => 'spammer',
+ 'message' => 'Success!'
+ ])
+ ->accessLevel('admin')
+ ->save();
+ break;
}
Cache::forget('_api:statuses:recent_9:' . $status->profile_id);
diff --git a/app/Jobs/ModPipeline/HandleSpammerPipeline.php b/app/Jobs/ModPipeline/HandleSpammerPipeline.php
new file mode 100644
index 000000000..0e5b4042d
--- /dev/null
+++ b/app/Jobs/ModPipeline/HandleSpammerPipeline.php
@@ -0,0 +1,52 @@
+profile = $profile;
+ }
+
+ public function handle()
+ {
+ $profile = $this->profile;
+
+ $profile->unlisted = true;
+ $profile->cw = true;
+ $profile->no_autolink = true;
+ $profile->save();
+
+ Status::whereProfileId($profile->id)
+ ->chunk(50, function($statuses) {
+ foreach($statuses as $status) {
+ $status->is_nsfw = true;
+ $status->scope = $status->scope === 'public' ? 'unlisted' : $status->scope;
+ $status->visibility = $status->scope;
+ $status->save();
+ StatusService::del($status->id);
+ }
+ });
+
+ Cache::forget('_api:statuses:recent_9:'.$profile->id);
+
+ return 1;
+ }
+}
diff --git a/resources/assets/js/components/partials/ContextMenu.vue b/resources/assets/js/components/partials/ContextMenu.vue
index 5a398ba74..35402d819 100644
--- a/resources/assets/js/components/partials/ContextMenu.vue
+++ b/resources/assets/js/components/partials/ContextMenu.vue
@@ -37,6 +37,10 @@
Unlist from Timelines
Remove Content Warning
Add Content Warning
+
+ Mark as Spammer
+ Unlist + CW existing and future posts
+
Cancel
@@ -465,99 +469,129 @@
moderatePost(status, action, $event) {
let username = status.account.username;
+ let pid = status.id;
let msg = '';
let self = this;
switch(action) {
case 'addcw':
- msg = 'Are you sure you want to add a content warning to this post?';
- swal({
- title: 'Confirm',
- text: msg,
- icon: 'warning',
- buttons: true,
- dangerMode: true
- }).then(res => {
- if(res) {
- axios.post('/api/v2/moderator/action', {
- action: action,
- item_id: status.id,
- item_type: 'status'
- }).then(res => {
- swal('Success', 'Successfully added content warning', 'success');
- status.sensitive = true;
- self.ctxModMenuClose();
- }).catch(err => {
- swal(
- 'Error',
- 'Something went wrong, please try again later.',
- 'error'
- );
- self.ctxModMenuClose();
- });
- }
- });
+ msg = 'Are you sure you want to add a content warning to this post?';
+ swal({
+ title: 'Confirm',
+ text: msg,
+ icon: 'warning',
+ buttons: true,
+ dangerMode: true
+ }).then(res => {
+ if(res) {
+ axios.post('/api/v2/moderator/action', {
+ action: action,
+ item_id: status.id,
+ item_type: 'status'
+ }).then(res => {
+ swal('Success', 'Successfully added content warning', 'success');
+ status.sensitive = true;
+ self.ctxModMenuClose();
+ }).catch(err => {
+ swal(
+ 'Error',
+ 'Something went wrong, please try again later.',
+ 'error'
+ );
+ self.ctxModMenuClose();
+ });
+ }
+ });
break;
case 'remcw':
- msg = 'Are you sure you want to remove the content warning on this post?';
- swal({
- title: 'Confirm',
- text: msg,
- icon: 'warning',
- buttons: true,
- dangerMode: true
- }).then(res => {
- if(res) {
- axios.post('/api/v2/moderator/action', {
- action: action,
- item_id: status.id,
- item_type: 'status'
- }).then(res => {
- swal('Success', 'Successfully added content warning', 'success');
- status.sensitive = false;
- self.ctxModMenuClose();
- }).catch(err => {
- swal(
- 'Error',
- 'Something went wrong, please try again later.',
- 'error'
- );
- self.ctxModMenuClose();
- });
- }
- });
+ msg = 'Are you sure you want to remove the content warning on this post?';
+ swal({
+ title: 'Confirm',
+ text: msg,
+ icon: 'warning',
+ buttons: true,
+ dangerMode: true
+ }).then(res => {
+ if(res) {
+ axios.post('/api/v2/moderator/action', {
+ action: action,
+ item_id: status.id,
+ item_type: 'status'
+ }).then(res => {
+ swal('Success', 'Successfully added content warning', 'success');
+ status.sensitive = false;
+ self.ctxModMenuClose();
+ }).catch(err => {
+ swal(
+ 'Error',
+ 'Something went wrong, please try again later.',
+ 'error'
+ );
+ self.ctxModMenuClose();
+ });
+ }
+ });
break;
case 'unlist':
- msg = 'Are you sure you want to unlist this post?';
- swal({
- title: 'Confirm',
- text: msg,
- icon: 'warning',
- buttons: true,
- dangerMode: true
- }).then(res => {
- if(res) {
- axios.post('/api/v2/moderator/action', {
- action: action,
- item_id: status.id,
- item_type: 'status'
- }).then(res => {
- this.feed = this.feed.filter(f => {
- return f.id != status.id;
+ msg = 'Are you sure you want to unlist this post?';
+ swal({
+ title: 'Confirm',
+ text: msg,
+ icon: 'warning',
+ buttons: true,
+ dangerMode: true
+ }).then(res => {
+ if(res) {
+ axios.post('/api/v2/moderator/action', {
+ action: action,
+ item_id: status.id,
+ item_type: 'status'
+ }).then(res => {
+ this.feed = this.feed.filter(f => {
+ return f.id != status.id;
+ });
+ swal('Success', 'Successfully unlisted post', 'success');
+ self.ctxModMenuClose();
+ }).catch(err => {
+ self.ctxModMenuClose();
+ swal(
+ 'Error',
+ 'Something went wrong, please try again later.',
+ 'error'
+ );
});
- swal('Success', 'Successfully unlisted post', 'success');
- self.ctxModMenuClose();
- }).catch(err => {
- self.ctxModMenuClose();
- swal(
- 'Error',
- 'Something went wrong, please try again later.',
- 'error'
- );
- });
- }
- });
+ }
+ });
+ break;
+
+ case 'spammer':
+ msg = 'Are you sure you want to mark this user as a spammer? All existing and future posts will be unlisted on timelines and a content warning will be applied.';
+ swal({
+ title: 'Confirm',
+ text: msg,
+ icon: 'warning',
+ buttons: true,
+ dangerMode: true
+ }).then(res => {
+ if(res) {
+ axios.post('/api/v2/moderator/action', {
+ action: action,
+ item_id: status.id,
+ item_type: 'status'
+ }).then(res => {
+ swal('Success', 'Successfully marked account as spammer', 'success');
+ self.ctxModMenuClose();
+ }).catch(err => {
+ self.ctxModMenuClose();
+ swal(
+ 'Error',
+ 'Something went wrong, please try again later.',
+ 'error'
+ );
+ });
+ }
+ });
break;
}
},