Add hashtag administration

This commit is contained in:
Daniel Supernault 2022-12-27 04:43:04 -07:00
parent f9341d0197
commit 8487231177
No known key found for this signature in database
GPG Key ID: 0DEF1C662C9033F7
12 changed files with 143 additions and 91 deletions

View File

@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers\Admin;
use Cache;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Hashtag;
use App\StatusHashtag;
use App\Http\Resources\AdminHashtag;
use App\Services\TrendingHashtagService;
trait AdminHashtagsController
{
public function hashtagsHome(Request $request)
{
return view('admin.hashtags.home');
}
public function hashtagsApi(Request $request)
{
$this->validate($request, [
'action' => 'sometimes|in:banned,nsfw',
'sort' => 'sometimes|in:id,name,cached_count,can_search,can_trend,is_banned,is_nsfw',
'dir' => 'sometimes|in:asc,desc'
]);
$action = $request->input('action');
$query = $request->input('q');
$sort = $request->input('sort');
$order = $request->input('dir');
$hashtags = Hashtag::when($query, function($q, $query) {
return $q->where('name', 'like', $query . '%');
})
->when($sort, function($q, $sort) use($order) {
return $q->orderBy($sort, $order);
}, function($q) {
return $q->orderByDesc('id');
})
->when($action, function($q, $action) {
if($action === 'banned') {
return $q->whereIsBanned(true);
} else if ($action === 'nsfw') {
return $q->whereIsNsfw(true);
}
})
->cursorPaginate(10)
->withQueryString();
return AdminHashtag::collection($hashtags);
}
public function hashtagsStats(Request $request)
{
$stats = [
'total_unique' => Hashtag::count(),
'total_posts' => StatusHashtag::count(),
'added_14_days' => Hashtag::where('created_at', '>', now()->subDays(14))->count(),
'total_banned' => Hashtag::whereIsBanned(true)->count(),
'total_nsfw' => Hashtag::whereIsNsfw(true)->count()
];
return response()->json($stats);
}
public function hashtagsGet(Request $request)
{
return new AdminHashtag(Hashtag::findOrFail($request->input('id')));
}
public function hashtagsUpdate(Request $request)
{
$this->validate($request, [
'id' => 'required',
'name' => 'required',
'slug' => 'required',
'can_search' => 'required:boolean',
'can_trend' => 'required:boolean',
'is_nsfw' => 'required:boolean',
'is_banned' => 'required:boolean'
]);
$hashtag = Hashtag::whereSlug($request->input('slug'))->findOrFail($request->input('id'));
$canTrendPrev = $hashtag->can_trend == null ? true : $hashtag->can_trend;
$hashtag->is_banned = $request->input('is_banned');
$hashtag->is_nsfw = $request->input('is_nsfw');
$hashtag->can_search = $hashtag->is_banned ? false : $request->input('can_search');
$hashtag->can_trend = $hashtag->is_banned ? false : $request->input('can_trend');
$hashtag->save();
TrendingHashtagService::refresh();
return new AdminHashtag($hashtag);
}
public function hashtagsClearTrendingCache(Request $request)
{
TrendingHashtagService::refresh();
return [];
}
}

View File

@ -12,6 +12,7 @@ use App\{
Profile,
Report,
Status,
StatusHashtag,
Story,
User
};
@ -22,6 +23,7 @@ use Illuminate\Support\Facades\Redis;
use App\Http\Controllers\Admin\{
AdminDirectoryController,
AdminDiscoverController,
AdminHashtagsController,
AdminInstanceController,
AdminReportController,
// AdminGroupsController,
@ -43,6 +45,7 @@ class AdminController extends Controller
use AdminReportController,
AdminDirectoryController,
AdminDiscoverController,
AdminHashtagsController,
// AdminGroupsController,
AdminMediaController,
AdminSettingsController,
@ -201,12 +204,6 @@ class AdminController extends Controller
return view('admin.apps.home', compact('apps'));
}
public function hashtagsHome(Request $request)
{
$hashtags = Hashtag::orderByDesc('id')->paginate(10);
return view('admin.hashtags.home', compact('hashtags'));
}
public function messagesHome(Request $request)
{
$messages = Contact::orderByDesc('id')->paginate(10);

View File

@ -24,6 +24,7 @@ use App\Services\ReblogService;
use App\Services\StatusHashtagService;
use App\Services\SnowflakeService;
use App\Services\StatusService;
use App\Services\TrendingHashtagService;
use App\Services\UserFilterService;
class DiscoverController extends Controller
@ -181,33 +182,7 @@ class DiscoverController extends Controller
{
abort_if(!$request->user(), 403);
$res = Cache::remember('api:discover:v1.1:trending:hashtags', 43200, function() {
$minId = StatusHashtag::where('created_at', '>', now()->subDays(14))->first();
if(!$minId) {
return [];
}
return StatusHashtag::select('hashtag_id', \DB::raw('count(*) as total'))
->where('id', '>', $minId->id)
->groupBy('hashtag_id')
->orderBy('total','desc')
->take(20)
->get()
->map(function($h) {
$hashtag = Hashtag::find($h->hashtag_id);
if(!$hashtag) {
return;
}
return [
'id' => $h->hashtag_id,
'total' => $h->total,
'name' => '#'.$hashtag->name,
'hashtag' => $hashtag->name,
'url' => $hashtag->url()
];
})
->filter()
->values();
});
$res = TrendingHashtagService::getTrending();
return $res;
}

View File

@ -96,16 +96,9 @@ class SearchApiV2Service
$query = substr($rawQuery, 1) . '%';
}
$banned = InstanceService::getBannedDomains();
$results = Profile::select('profiles.*', 'followers.profile_id', 'followers.created_at')
->whereNull('status')
->leftJoin('followers', function($join) use($user) {
return $join->on('profiles.id', '=', 'followers.following_id')
->where('followers.profile_id', $user->profile_id);
})
$results = Profile::select('username', 'id', 'followers_count', 'domain')
->where('username', 'like', $query)
->orderBy('domain')
->orderByDesc('profiles.followers_count')
->orderByDesc('followers.created_at')
->offset($offset)
->limit($limit)
->get()
@ -131,7 +124,7 @@ class SearchApiV2Service
$limit = $this->query->input('limit') ?? 20;
$offset = $this->query->input('offset') ?? 0;
$query = '%' . $this->query->input('q') . '%';
return Hashtag::whereIsBanned(false)
return Hashtag::where('can_search', true)
->where('name', 'like', $query)
->offset($offset)
->limit($limit)

File diff suppressed because one or more lines are too long

2
public/js/admin.js vendored

File diff suppressed because one or more lines are too long

2
public/js/vendor.js vendored

File diff suppressed because one or more lines are too long

View File

@ -49,7 +49,7 @@
*/
/*!
* Pusher JavaScript Library v7.5.0
* Pusher JavaScript Library v7.6.0
* https://pusher.com/
*
* Copyright 2020, Pusher
@ -65,14 +65,14 @@
*/
/*!
* Sizzle CSS Selector Engine v2.3.6
* Sizzle CSS Selector Engine v2.3.8
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
* Date: 2021-02-16
* Date: 2022-11-16
*/
/*!
@ -82,7 +82,7 @@
*/
/*!
* jQuery JavaScript Library v3.6.1
* jQuery JavaScript Library v3.6.2
* https://jquery.com/
*
* Includes Sizzle.js
@ -92,7 +92,7 @@
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2022-08-26T17:52Z
* Date: 2022-12-13T14:56Z
*/
/*!

View File

@ -16,7 +16,7 @@
"/js/profile-directory.js": "/js/profile-directory.js?id=62b575734ca1d8e8b780b5dbcde82680",
"/js/story-compose.js": "/js/story-compose.js?id=9d606ec8de7ba57ed1402c531a3937ed",
"/js/direct.js": "/js/direct.js?id=83f62237dcbdcd3c3b0dd97ebb8cf4aa",
"/js/admin.js": "/js/admin.js?id=145e57a8fe4986cf8fce7378284e8c1f",
"/js/admin.js": "/js/admin.js?id=09ff5d52a465c7c7e9e04209eeb76df6",
"/js/rempro.js": "/js/rempro.js?id=61bb49ccfe70d28ed788750f9c6279b2",
"/js/rempos.js": "/js/rempos.js?id=da10eddc2edd1d3a29d8ffcd75d239dc",
"/js/live-player.js": "/js/live-player.js?id=674d2b72d4cf417d9d7a3953c55f37ca",
@ -43,8 +43,8 @@
"/css/appdark.css": "/css/appdark.css?id=de85ecce91d9ed7afa7714547eb1e26c",
"/css/app.css": "/css/app.css?id=88a0a931d5b0e24b0d9355f548414768",
"/css/portfolio.css": "/css/portfolio.css?id=db2c9929a56d83f9ff2aaf2161d29d36",
"/css/admin.css": "/css/admin.css?id=c39d4fbc91a140c22cf5afe5d9faa827",
"/css/admin.css": "/css/admin.css?id=619b6c6613a24e232048856e72110862",
"/css/landing.css": "/css/landing.css?id=e852a642699916fc9ff8208d7e06daa8",
"/css/spa.css": "/css/spa.css?id=602c4f74ce800b7bf45a8d8a4d8cb6e5",
"/js/vendor.js": "/js/vendor.js?id=cedafb53a2de5dd37758d3009b4b21c1"
"/js/vendor.js": "/js/vendor.js?id=be64338fb941b8e58b836490ef0e96be"
}

View File

@ -20,3 +20,13 @@ Chart.defaults.global.defaultFontFamily = "-apple-system,BlinkMacSystemFont,Sego
Array.from(document.querySelectorAll('.pagination .page-link'))
.filter(el => el.textContent === '« Previous' || el.textContent === 'Next »')
.forEach(el => el.textContent = (el.textContent === 'Next »' ? '' :''));
Vue.component(
'admin-directory',
require('./../components/admin/AdminDirectory.vue').default
);
Vue.component(
'hashtag-component',
require('./../components/admin/AdminHashtags.vue').default
);

View File

@ -1,43 +1,13 @@
@extends('admin.partial.template-full')
@section('section')
<div class="title">
<h3 class="font-weight-bold d-inline-block">Hashtags</h3>
</div>
<hr>
<table class="table table-responsive">
<thead class="bg-light">
<tr>
<th scope="col" width="10%">#</th>
<th scope="col" width="30%">Hashtag</th>
<th scope="col" width="15%">Status Count</th>
<th scope="col" width="10%">NSFW</th>
<th scope="col" width="10%">Banned</th>
<th scope="col" width="15%">Created</th>
</tr>
</thead>
<tbody>
@foreach($hashtags as $tag)
<tr>
<td>
<a href="/i/admin/apps/show/{{$tag->id}}" class="btn btn-sm btn-outline-primary">
{{$tag->id}}
</a>
</td>
<td class="font-weight-bold">{{$tag->name}}</td>
<td class="font-weight-bold text-center">
<a href="{{$tag->url()}}">
{{$tag->posts()->count()}}
</a>
</td>
<td class="font-weight-bold">{{$tag->is_nsfw ? 'true' : 'false'}}</td>
<td class="font-weight-bold">{{$tag->is_banned ? 'true' : 'false'}}</td>
<td class="font-weight-bold">{{$tag->created_at->diffForHumans()}}</td>
</tr>
@endforeach
</tbody>
</table>
<div class="d-flex justify-content-center mt-5 small">
{{$hashtags->links()}}
</div>
<hashtag-component />
@endsection
@push('scripts')
<script type="text/javascript">
new Vue({ el: '#panel'});
</script>
@endpush

View File

@ -108,6 +108,11 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
Route::post('directory/testimonial/save', 'AdminController@directorySaveTestimonial');
Route::post('directory/testimonial/delete', 'AdminController@directoryDeleteTestimonial');
Route::post('directory/testimonial/update', 'AdminController@directoryUpdateTestimonial');
Route::get('hashtags/stats', 'AdminController@hashtagsStats');
Route::get('hashtags/query', 'AdminController@hashtagsApi');
Route::get('hashtags/get', 'AdminController@hashtagsGet');
Route::post('hashtags/update', 'AdminController@hashtagsUpdate');
Route::post('hashtags/clear-trending-cache', 'AdminController@hashtagsClearTrendingCache');
});
});