diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index cd38e2fc7..5bbb73df8 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -14,7 +14,7 @@ use App\{ Story, User }; -use DB, Cache; +use DB, Cache, Storage; use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Redis; @@ -22,8 +22,10 @@ use App\Http\Controllers\Admin\{ AdminDiscoverController, AdminInstanceController, AdminReportController, + // AdminGroupsController, AdminMediaController, AdminSettingsController, + // AdminStorageController, AdminSupportController, AdminUserController }; @@ -31,14 +33,17 @@ use Illuminate\Validation\Rule; use App\Services\AdminStatsService; use App\Services\StatusService; use App\Services\StoryService; +use App\Models\CustomEmoji; class AdminController extends Controller { use AdminReportController, AdminDiscoverController, + // AdminGroupsController, AdminMediaController, AdminSettingsController, AdminInstanceController, + // AdminStorageController, AdminUserController; public function __construct() @@ -343,4 +348,109 @@ class AdminController extends Controller $stats = StoryService::adminStats(); return view('admin.stories.home', compact('stories', 'stats')); } + + public function customEmojiHome(Request $request) + { + if(!config('federation.custom_emoji.enabled')) { + return view('admin.custom-emoji.not-enabled'); + } + $this->validate($request, [ + 'sort' => 'sometimes|in:all,local,remote,duplicates,disabled' + ]); + + if($request->has('cc')) { + Cache::forget('pf:admin:custom_emoji:stats'); + return redirect(route('admin.custom-emoji')); + } + + $sort = $request->input('sort') ?? 'all'; + $emojis = CustomEmoji::when($sort, function($query, $sort) { + if($sort == 'all') { + return $query->groupBy('shortcode')->latest(); + } else if($sort == 'local') { + return $query->latest()->where('domain', '=', config('pixelfed.domain.app')); + } else if($sort == 'remote') { + return $query->latest()->where('domain', '!=', config('pixelfed.domain.app')); + } else if($sort == 'duplicates') { + return $query->latest()->groupBy('shortcode')->havingRaw('count(*) > 1'); + } else if($sort == 'disabled') { + return $query->latest()->whereDisabled(true); + } + })->cursorPaginate(10); + + $stats = Cache::remember('pf:admin:custom_emoji:stats', 43200, function() { + return [ + 'total' => CustomEmoji::count(), + 'active' => CustomEmoji::whereDisabled(false)->count(), + 'remote' => CustomEmoji::where('domain', '!=', config('pixelfed.domain.app'))->count(), + 'duplicate' => CustomEmoji::groupBy('shortcode')->havingRaw('count(*) > 1')->count() + ]; + }); + + return view('admin.custom-emoji.home', compact('emojis', 'sort', 'stats')); + } + + public function customEmojiToggleActive(Request $request, $id) + { + abort_unless(config('federation.custom_emoji.enabled'), 404); + $emoji = CustomEmoji::findOrFail($id); + $emoji->disabled = !$emoji->disabled; + $emoji->save(); + $key = CustomEmoji::CACHE_KEY . str_replace(':', '', $emoji->shortcode); + Cache::forget($key); + return redirect()->back(); + } + + public function customEmojiAdd(Request $request) + { + abort_unless(config('federation.custom_emoji.enabled'), 404); + return view('admin.custom-emoji.add'); + } + + public function customEmojiStore(Request $request) + { + abort_unless(config('federation.custom_emoji.enabled'), 404); + $this->validate($request, [ + 'shortcode' => [ + 'required', + 'min:3', + 'max:80', + 'starts_with::', + 'ends_with::', + Rule::unique('custom_emoji')->where(function ($query) use($request) { + return $query->whereDomain(config('pixelfed.domain.app')) + ->whereShortcode($request->input('shortcode')); + }) + ], + 'emoji' => 'required|file|mimes:jpg,png|max:' . (config('federation.custom_emoji.max_size') / 1000) + ]); + + $emoji = new CustomEmoji; + $emoji->shortcode = $request->input('shortcode'); + $emoji->domain = config('pixelfed.domain.app'); + $emoji->save(); + + $fileName = $emoji->id . '.' . $request->emoji->extension(); + $request->emoji->storeAs('public/emoji', $fileName); + $emoji->media_path = 'emoji/' . $fileName; + $emoji->save(); + return redirect(route('admin.custom-emoji')); + } + + public function customEmojiDelete(Request $request, $id) + { + abort_unless(config('federation.custom_emoji.enabled'), 404); + $emoji = CustomEmoji::findOrFail($id); + Storage::delete("public/{$emoji->media_path}"); + $emoji->delete(); + return redirect(route('admin.custom-emoji')); + } + + public function customEmojiShowDuplicates(Request $request, $id) + { + abort_unless(config('federation.custom_emoji.enabled'), 404); + $emoji = CustomEmoji::orderBy('id')->whereDisabled(false)->whereShortcode($id)->firstOrFail(); + $emojis = CustomEmoji::whereShortcode($id)->where('id', '!=', $emoji->id)->cursorPaginate(10); + return view('admin.custom-emoji.duplicates', compact('emoji', 'emojis')); + } } diff --git a/app/Models/CustomEmoji.php b/app/Models/CustomEmoji.php index 3b7c38a1d..9d4ed1bdd 100644 --- a/app/Models/CustomEmoji.php +++ b/app/Models/CustomEmoji.php @@ -24,7 +24,7 @@ class CustomEmoji extends Model ->matchAll(self::SCAN_RE) ->map(function($match) use($activitypub) { $tag = Cache::remember(self::CACHE_KEY . $match, 14400, function() use($match) { - return self::whereShortcode(':' . $match . ':')->first(); + return self::orderBy('id')->whereDisabled(false)->whereShortcode(':' . $match . ':')->first(); }); if($tag) { diff --git a/resources/views/admin/custom-emoji/add.blade.php b/resources/views/admin/custom-emoji/add.blade.php new file mode 100644 index 000000000..8eba9fe09 --- /dev/null +++ b/resources/views/admin/custom-emoji/add.blade.php @@ -0,0 +1,63 @@ +@extends('admin.partial.template-full') + +@section('section') + +
+
+
+
+
+

Add Custom Emoji

+
+
+
+
+
+
+
+
+ @if ($errors->any()) + @foreach ($errors->all() as $error) +
+

{{ $error }}

+
+ @endforeach + @endif + +
+
+ New Custom Emoji +
+ +
+
+ @csrf +
+ + +

Must start and end with :

+
+ +
+ + +

Must be a png or jpg under

+
+
+ +
+
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/admin/custom-emoji/duplicates.blade.php b/resources/views/admin/custom-emoji/duplicates.blade.php new file mode 100644 index 000000000..51ff3a714 --- /dev/null +++ b/resources/views/admin/custom-emoji/duplicates.blade.php @@ -0,0 +1,101 @@ +@extends('admin.partial.template-full') + +@section('section') + +
+
+
+
+
+

Custom Emoji

+

Showing duplicates of {{$emoji->shortcode}}

+
+
+
+
+
+
+
+
+
+

+ Duplicate emoji shortcodes can lead to unpredictible results +

+

If you change the primary/in-use emoji, you will need to clear the cache by running the php artisan cache:clear command for the changes to take effect immediately.

+
+ +

In Use

+
+
+
+ + +
+

{{ $emoji->shortcode }}

+

{{ $emoji->domain }}

+
+ +
Added {{$emoji->created_at->diffForHumans(null, true, true)}}
+ +
+ @csrf + +
+ + + +
+
+
+
+

Not used (due to conflicting shortcode)

+
+ @foreach($emojis as $emoji) +
+
+ + +
+

{{ $emoji->shortcode }}

+

{{ $emoji->domain }}

+
+ +
Added {{$emoji->created_at->diffForHumans(null, true, true)}}
+ +
+ @csrf + +
+ + + +
+
+ @endforeach +
+ +
+ {{ $emojis->links() }} +
+
+
+
+@endsection diff --git a/resources/views/admin/custom-emoji/home.blade.php b/resources/views/admin/custom-emoji/home.blade.php new file mode 100644 index 000000000..18b1933a1 --- /dev/null +++ b/resources/views/admin/custom-emoji/home.blade.php @@ -0,0 +1,155 @@ +@extends('admin.partial.template-full') + +@section('section') + +
+
+
+
+
+

Custom Emoji

+
+
+
+
+
+
Total Emoji
+ {{$stats['total']}} +
+
+
+
+
Total Active
+ {{$stats['active']}} +
+
+
+
+
Remote Emoji
+ {{$stats['remote']}} +
+
+
+
+
Duplicate Emoji
+ {{$stats['duplicate']}} +
+
+ +
+
+
+

+ Stats are cached for 12 hours and may not reflect the latest data.
To refresh the cache and view the most recent data, click here. +

+
+
+
+
+
+
+
+
+ + + + @if($sort == 'duplicates') +
+

+ Duplicate emoji shortcodes can lead to unpredictible results +

+
+ @endif + +
+ @foreach($emojis as $emoji) +
+
+ + +
+

{{ $emoji->shortcode }}

+

{{ $emoji->domain }}

+
+ + @if($sort == 'duplicates') + + View duplicates + + {{--
Updated {{$emoji->updated_at->diffForHumans(null, true, true)}}
--}} + @else +
Updated {{$emoji->updated_at->diffForHumans(null, true, true)}}
+ +
+ @csrf + +
+ + + @endif + +
+
+ @endforeach +
+ +
+ {{ $emojis->links() }} +
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/admin/custom-emoji/not-enabled.blade.php b/resources/views/admin/custom-emoji/not-enabled.blade.php new file mode 100644 index 000000000..3fa928406 --- /dev/null +++ b/resources/views/admin/custom-emoji/not-enabled.blade.php @@ -0,0 +1,24 @@ +@extends('admin.partial.template-full') + +@section('section') + +
+
+
+
+
+

Custom Emoji

+
+
+
+
+
+
+
+
+

This feature is not enabled

+

To enable this feature, set CUSTOM_EMOJI=true in
your .env file and run php artisan config:cache

+
+
+
+@endsection diff --git a/resources/views/admin/partial/sidenav.blade.php b/resources/views/admin/partial/sidenav.blade.php index ff204756e..e63852e56 100644 --- a/resources/views/admin/partial/sidenav.blade.php +++ b/resources/views/admin/partial/sidenav.blade.php @@ -1,7 +1,7 @@