mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-09 16:24:51 +00:00
Add MediaBlocklist feature
This commit is contained in:
parent
c2d194af1d
commit
ba1f7e7e2c
7 changed files with 206 additions and 44 deletions
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
|
||||||
use DB, Cache;
|
use DB, Cache;
|
||||||
use App\{
|
use App\{
|
||||||
Media,
|
Media,
|
||||||
|
MediaBlocklist,
|
||||||
Profile,
|
Profile,
|
||||||
Status
|
Status
|
||||||
};
|
};
|
||||||
|
@ -21,8 +22,8 @@ trait AdminMediaController
|
||||||
'nullable',
|
'nullable',
|
||||||
'string',
|
'string',
|
||||||
'min:1',
|
'min:1',
|
||||||
'max:4',
|
'max:13',
|
||||||
Rule::in(['grid','list'])
|
Rule::in(['grid','list', 'banned', 'addbanned'])
|
||||||
],
|
],
|
||||||
'search' => 'nullable|string|min:1|max:20'
|
'search' => 'nullable|string|min:1|max:20'
|
||||||
]);
|
]);
|
||||||
|
@ -34,9 +35,14 @@ trait AdminMediaController
|
||||||
->whereIn('profile_id', $profiles)
|
->whereIn('profile_id', $profiles)
|
||||||
->orWhere('mime', $request->input('search'))
|
->orWhere('mime', $request->input('search'))
|
||||||
->paginate(12);
|
->paginate(12);
|
||||||
} else {
|
return view('admin.media.home', compact('media'));
|
||||||
$media = Media::whereHas('status')->with('status')->orderby('id', 'desc')->paginate(12);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($request->input('layout') == 'banned') {
|
||||||
|
$media = MediaBlocklist::latest()->paginate(12);
|
||||||
|
return view('admin.media.home', compact('media'));
|
||||||
|
}
|
||||||
|
$media = Media::whereHas('status')->with('status')->orderby('id', 'desc')->paginate(12);
|
||||||
return view('admin.media.home', compact('media'));
|
return view('admin.media.home', compact('media'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
50
app/Http/Controllers/MediaBlocklistController.php
Normal file
50
app/Http/Controllers/MediaBlocklistController.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\MediaBlocklist;
|
||||||
|
|
||||||
|
class MediaBlocklistController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
$this->middleware('admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'hash' => 'required|string|size:64',
|
||||||
|
'name' => 'nullable|string',
|
||||||
|
'description' => 'nullable|string|max:500',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$hash = $request->input('hash');
|
||||||
|
abort_if(preg_match("/^([a-f0-9]{64})$/", $hash) !== 1, 400);
|
||||||
|
|
||||||
|
$name = $request->input('name');
|
||||||
|
$description = $request->input('description');
|
||||||
|
|
||||||
|
$mb = new MediaBlocklist;
|
||||||
|
$mb->sha256 = $hash;
|
||||||
|
$mb->name = $name;
|
||||||
|
$mb->description = $description;
|
||||||
|
$mb->save();
|
||||||
|
|
||||||
|
return redirect('/i/admin/media?layout=banned');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'id' => 'required|integer'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$media = MediaBlocklist::findOrFail($request->input('id'));
|
||||||
|
$media->delete();
|
||||||
|
|
||||||
|
return redirect('/i/admin/media?layout=banned');
|
||||||
|
}
|
||||||
|
}
|
10
app/MediaBlocklist.php
Normal file
10
app/MediaBlocklist.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class MediaBlocklist extends Model
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateMediaBlocklistsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('media_blocklists', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('sha256')->nullable()->unique()->index();
|
||||||
|
$table->string('sha512')->nullable()->unique()->index();
|
||||||
|
$table->string('name')->nullable();
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->boolean('active')->default(true)->index();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('media_blocklists');
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,11 @@
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h3 class="font-weight-bold d-inline-block">Media</h3>
|
<h3 class="font-weight-bold d-inline-block">Media</h3>
|
||||||
<span class="float-right">
|
<span class="float-right">
|
||||||
<a class="btn btn-{{request()->input('layout')!=='list'?'primary':'light'}} btn-sm" href="{{route('admin.media')}}">
|
<a class="btn btn-{{request()->input('layout')!=='banned'?'primary':'light'}} btn-sm font-weight-bold" href="{{route('admin.media')}}">
|
||||||
<i class="fas fa-th"></i>
|
All
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-{{request()->input('layout')=='list'?'primary':'light'}} btn-sm mr-3" href="{{route('admin.media',['layout'=>'list', 'page' => request()->input('page') ?? 1])}}">
|
<a class="btn btn-{{request()->input('layout')=='banned'?'primary':'light'}} btn-sm mr-3 font-weight-bold" href="{{route('admin.media',['layout'=>'banned', 'page' => request()->input('page') ?? 1])}}">
|
||||||
<i class="fas fa-list"></i>
|
Banned
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown d-inline-block">
|
<div class="dropdown d-inline-block">
|
||||||
<button class="btn btn-light btn-sm dropdown-toggle font-weight-bold" type="button" id="filterDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button class="btn btn-light btn-sm dropdown-toggle font-weight-bold" type="button" id="filterDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
@ -16,8 +16,8 @@
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="filterDropdown" style="width: 300px;">
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="filterDropdown" style="width: 300px;">
|
||||||
<div class="dropdown-item">
|
<div class="dropdown-item">
|
||||||
<form>
|
<form action="/i/admin/media/?page=1">
|
||||||
<input type="hidden" name="layout" value="{{request()->input('layout')}}"></input>
|
<input type="hidden" name="layout" value=""></input>
|
||||||
<input type="hidden" name="page" value="{{request()->input('page')}}"></input>
|
<input type="hidden" name="page" value="{{request()->input('page')}}"></input>
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<input class="form-control" name="search" placeholder="Filter by username, mime type" autocomplete="off"></input>
|
<input class="form-control" name="search" placeholder="Filter by username, mime type" autocomplete="off"></input>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-divider"></div>
|
{{-- <div class="dropdown-divider"></div>
|
||||||
<p class="text-wrap p-1 p-md-3 text-center">
|
<p class="text-wrap p-1 p-md-3 text-center">
|
||||||
<a class="badge badge-primary p-2 mb-2 btn-filter" href="#" data-filter="cw" data-filter-state="true" data-toggle="tooltip" title="Show Content Warning media">CW</a>
|
<a class="badge badge-primary p-2 mb-2 btn-filter" href="#" data-filter="cw" data-filter-state="true" data-toggle="tooltip" title="Show Content Warning media">CW</a>
|
||||||
<a class="badge badge-primary p-2 mb-2 btn-filter" href="#" data-filter="remote" data-filter-state="true" data-toggle="tooltip" title="Show remote media">Remote Media</a>
|
<a class="badge badge-primary p-2 mb-2 btn-filter" href="#" data-filter="remote" data-filter-state="true" data-toggle="tooltip" title="Show remote media">Remote Media</a>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<a class="badge badge-light p-2 mb-2 btn-filter" href="#" data-filter="banned" data-filter-state="false" data-toggle="tooltip" title="Show banned media">Banned</a>
|
<a class="badge badge-light p-2 mb-2 btn-filter" href="#" data-filter="banned" data-filter-state="false" data-toggle="tooltip" title="Show banned media">Banned</a>
|
||||||
<a class="badge badge-light p-2 mb-2 btn-filter" href="#" data-filter="reported" data-filter-state="false" data-toggle="tooltip" title="Show reported media">Reported</a>
|
<a class="badge badge-light p-2 mb-2 btn-filter" href="#" data-filter="reported" data-filter-state="false" data-toggle="tooltip" title="Show reported media">Reported</a>
|
||||||
<a class="badge badge-light p-2 mb-2 btn-filter" href="#" data-filter="unlisted" data-filter-state="false" data-toggle="tooltip" title="Show unlisted media">Unlisted</a>
|
<a class="badge badge-light p-2 mb-2 btn-filter" href="#" data-filter="unlisted" data-filter-state="false" data-toggle="tooltip" title="Show unlisted media">Unlisted</a>
|
||||||
</p>
|
</p> --}}
|
||||||
{{-- <div class="dropdown-divider"></div>
|
{{-- <div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item font-weight-light" href="?filter=local&layout={{request()->input('layout')}}">Local Media Only</a>
|
<a class="dropdown-item font-weight-light" href="?filter=local&layout={{request()->input('layout')}}">Local Media Only</a>
|
||||||
<a class="dropdown-item font-weight-light" href="?filter=remote&layout={{request()->input('layout')}}">Remote Media Only</a>
|
<a class="dropdown-item font-weight-light" href="?filter=remote&layout={{request()->input('layout')}}">Remote Media Only</a>
|
||||||
|
@ -54,49 +54,103 @@
|
||||||
<p class="h4 pb-3">Showing results for: <i>{{request()->input('search')}}</i></p>
|
<p class="h4 pb-3">Showing results for: <i>{{request()->input('search')}}</i></p>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(request()->input('layout') == 'list')
|
@if(request()->input('layout') == 'banned')
|
||||||
|
<p class="text-right">
|
||||||
|
<a class="btn btn-primary py-0 px-5" href="/i/admin/media/?layout=addbanned">Add Banned Media</a>
|
||||||
|
</p>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
@foreach($media as $status)
|
@foreach($media as $b)
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div class="d-flex align-items-center">
|
||||||
<a class="font-weight-lighter small mr-3" href="/i/admin/media/show/{{$status->id}}">{{$status->id}}</a>
|
<span class="mr-4 text-monospace small">
|
||||||
<a href="{{$status->url()}}">
|
{{$b->id}}
|
||||||
<img class="" src="{{$status->thumb()}}" width="60px" height="60px">
|
</span>
|
||||||
</a>
|
<span class="d-inline-block">
|
||||||
|
<p class="mb-0 small text-monospace">{{$b->sha256}}</p>
|
||||||
|
<p class="mb-0 font-weight-bold">{{$b->name ?? 'Untitled'}}</p>
|
||||||
|
<p class="mb-0 small">{{$b->description ?? 'No description'}}</p>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="small font-weight-bold">
|
||||||
<p class="mb-0 small">status id: <a href="{{$status->status->url()}}" class="font-weight-bold">{{$status->status_id}}</a></p>
|
{{$b->created_at->diffForHumans()}}
|
||||||
<p class="mb-0 small">username: <a href="{{$status->profile->url()}}" class="font-weight-bold">{{$status->profile->username}}</a></p>
|
|
||||||
<p class="mb-0 small">size: <span class="filesize font-weight-bold" data-size="{{$status->size}}">0</span></p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="">
|
||||||
<p class="mb-0 small">mime: <span class="font-weight-bold">{{$status->mime}}</span></p>
|
<form action="/i/admin/media/block/delete" method="post">
|
||||||
<p class="mb-0 small">content warning: <i class="fas {{$status->is_nsfw ? 'fa-check text-danger':'fa-times text-success'}}"></i></p>
|
@csrf
|
||||||
<p class="mb-0 small">
|
<input type="hidden" name="id" value="{{$b->id}}">
|
||||||
remote media: <i class="fas {{$status->remote_media ? 'fa-check text-danger':'fa-times text-success'}}"></i></p>
|
<button type="submit" class="btn btn-outline-danger">
|
||||||
</div>
|
<i class="fas fa-trash-alt"></i>
|
||||||
<div>
|
</button>
|
||||||
<a class="btn btn-outline-secondary btn-sm py-0" href="#">Actions</a>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
</ul>
|
</ul>
|
||||||
<hr>
|
|
||||||
<div class="d-flex justify-content-center">
|
@elseif(request()->input('layout') == 'addbanned')
|
||||||
{{$media->appends(['layout'=>request()->layout])->links()}}
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6 offset-md-3">
|
||||||
|
<div class="card shadow-none border">
|
||||||
|
<div class="card-header font-weight-bold">Add Banned Media</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" action="/i/admin/media/block/add">
|
||||||
|
@csrf
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="input3" class="text-muted font-weight-bold">SHA256 Hash</label>
|
||||||
|
<input type="text" class="form-control" id="input3" aria-describedby="input3Help" name="hash">
|
||||||
|
<small id="input3Help" class="form-text text-muted">Required</small>
|
||||||
</div>
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="input1" class="text-muted font-weight-bold">Name</label>
|
||||||
|
<input type="text" class="form-control" id="input1" aria-describedby="input1Help" name="name">
|
||||||
|
<small id="input1Help" class="form-text text-muted">Optional</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="input2" class="text-muted font-weight-bold">Description</label>
|
||||||
|
<textarea class="form-control" id="input2" aria-describedby="input2Help" rows="3" name="description"></textarea>
|
||||||
|
<small id="input2Help" class="form-text text-muted">Optional</small>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block font-weight-bold">Ban</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@else
|
@else
|
||||||
<div class="profile-timeline mt-5 row">
|
|
||||||
|
<ul class="list-group">
|
||||||
@foreach($media as $status)
|
@foreach($media as $status)
|
||||||
<div class="col-12 col-md-4 mb-4">
|
<li class="list-group-item">
|
||||||
<a class="card" href="{{$status->status->url()}}">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<img class="card-img-top" src="{{$status->thumb()}}" width="150px" height="150px">
|
<div>
|
||||||
|
<a class="font-weight-lighter small mr-3 text-monospace" href="/i/admin/media/show/{{$status->id}}">{{$status->id}}</a>
|
||||||
|
<a href="{{$status->url()}}">
|
||||||
|
<img class="" src="{{$status->thumb()}}" width="60px" height="60px">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
<div>
|
||||||
|
<p class="mb-0 small">status id: <a href="/p/{{\App\Services\HashidService::encode($status->status_id)}}" class="font-weight-bold text-monospace">{{$status->status_id}}</a></p>
|
||||||
|
<p class="mb-0 small">profile id: <a href="/i/admin/profiles/edit/{{$status->profile_id}}" class="font-weight-bold text-monospace">{{$status->profile_id}}</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 small">size: <span class="filesize font-weight-bold" data-size="{{$status->size}}">0</span></p>
|
||||||
|
<p class="mb-0 small">mime: <span class="font-weight-bold">{{$status->mime}}</span></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 small">content warning: <i class="fas {{$status->is_nsfw ? 'fa-check text-danger':'fa-times text-dark'}}"></i></p>
|
||||||
|
<p class="mb-0 small">
|
||||||
|
remote media: <i class="fas {{$status->remote_media ? 'fa-check text-danger':'fa-times text-dark'}}"></i></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="d-flex justify-content-center">
|
<div class="d-flex justify-content-center">
|
||||||
{{$media->appends(['layout'=>request()->layout])->links()}}
|
{{$media->appends(['layout'=>request()->layout])->links()}}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-8 offset-md-2">
|
<div class="col-12 col-md-8 offset-md-2">
|
||||||
<div class="card">
|
<div class="card shadow-none border">
|
||||||
<img class="card-img-top" src="{{$media->thumb()}}">
|
<img class="card-img-top" src="{{$media->thumb()}}">
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
<li class="list-group-item d-flex justify-content-between">
|
<li class="list-group-item d-flex justify-content-between">
|
||||||
|
@ -26,6 +26,9 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="mt-3 small text-muted">
|
||||||
|
SHA256 Hash: <span class="text-monospace text-dark">{{$media->original_sha256}}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -278,6 +278,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::get('redirect', 'SiteController@redirectUrl');
|
Route::get('redirect', 'SiteController@redirectUrl');
|
||||||
|
Route::post('admin/media/block/add', 'MediaBlocklistController@add');
|
||||||
|
Route::post('admin/media/block/delete', 'MediaBlocklistController@delete');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['prefix' => 'account'], function () {
|
Route::group(['prefix' => 'account'], function () {
|
||||||
|
|
Loading…
Reference in a new issue