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

View file

@ -24,6 +24,7 @@ use App\Services\ReblogService;
use App\Services\StatusHashtagService; use App\Services\StatusHashtagService;
use App\Services\SnowflakeService; use App\Services\SnowflakeService;
use App\Services\StatusService; use App\Services\StatusService;
use App\Services\TrendingHashtagService;
use App\Services\UserFilterService; use App\Services\UserFilterService;
class DiscoverController extends Controller class DiscoverController extends Controller
@ -181,33 +182,7 @@ class DiscoverController extends Controller
{ {
abort_if(!$request->user(), 403); abort_if(!$request->user(), 403);
$res = Cache::remember('api:discover:v1.1:trending:hashtags', 43200, function() { $res = TrendingHashtagService::getTrending();
$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();
});
return $res; return $res;
} }

View file

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

BIN
public/css/admin.css vendored

Binary file not shown.

BIN
public/js/admin.js vendored

Binary file not shown.

BIN
public/js/vendor.js vendored

Binary file not shown.

View file

@ -49,7 +49,7 @@
*/ */
/*! /*!
* Pusher JavaScript Library v7.5.0 * Pusher JavaScript Library v7.6.0
* https://pusher.com/ * https://pusher.com/
* *
* Copyright 2020, Pusher * Copyright 2020, Pusher
@ -65,14 +65,14 @@
*/ */
/*! /*!
* Sizzle CSS Selector Engine v2.3.6 * Sizzle CSS Selector Engine v2.3.8
* https://sizzlejs.com/ * https://sizzlejs.com/
* *
* Copyright JS Foundation and other contributors * Copyright JS Foundation and other contributors
* Released under the MIT license * Released under the MIT license
* https://js.foundation/ * 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/ * https://jquery.com/
* *
* Includes Sizzle.js * Includes Sizzle.js
@ -92,7 +92,7 @@
* Released under the MIT license * Released under the MIT license
* https://jquery.org/license * https://jquery.org/license
* *
* Date: 2022-08-26T17:52Z * Date: 2022-12-13T14:56Z
*/ */
/*! /*!

Binary file not shown.

View file

@ -20,3 +20,13 @@ Chart.defaults.global.defaultFontFamily = "-apple-system,BlinkMacSystemFont,Sego
Array.from(document.querySelectorAll('.pagination .page-link')) Array.from(document.querySelectorAll('.pagination .page-link'))
.filter(el => el.textContent === '« Previous' || el.textContent === 'Next »') .filter(el => el.textContent === '« Previous' || el.textContent === 'Next »')
.forEach(el => el.textContent = (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') @extends('admin.partial.template-full')
@section('section') @section('section')
<div class="title">
<h3 class="font-weight-bold d-inline-block">Hashtags</h3>
</div> </div>
<hr> <hashtag-component />
<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>
@endsection @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/save', 'AdminController@directorySaveTestimonial');
Route::post('directory/testimonial/delete', 'AdminController@directoryDeleteTestimonial'); Route::post('directory/testimonial/delete', 'AdminController@directoryDeleteTestimonial');
Route::post('directory/testimonial/update', 'AdminController@directoryUpdateTestimonial'); 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');
}); });
}); });