You've already forked Atomcms-edit
Initial commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Game\Player\UserCurrency;
|
||||
use App\Models\Game\Player\UserSetting;
|
||||
use App\Models\User;
|
||||
use App\Services\Community\StaffService;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class LeaderboardController extends Controller
|
||||
{
|
||||
/** @var array<int, int> */
|
||||
protected array $staffIds = [];
|
||||
|
||||
public function __construct(private readonly StaffService $staffService)
|
||||
{
|
||||
$this->staffIds = $this->staffService->fetchEmployeeIds();
|
||||
}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
$topCredits = Cache::remember('leaderboard_credits', 60, fn () => User::query()
|
||||
->whereNotIn('id', $this->staffIds)
|
||||
->orderByDesc('credits')
|
||||
->take(9)
|
||||
->get(['id', 'username', 'look', 'credits']));
|
||||
|
||||
$duckets = Cache::remember('leaderboard_duckets', 60, fn () => UserCurrency::query()
|
||||
->whereNotIn('user_id', $this->staffIds)
|
||||
->where('type', 0)
|
||||
->orderByDesc('amount')
|
||||
->take(9)
|
||||
->with(['user' => fn ($q) => $q->select('id', 'username', 'look')])
|
||||
->get());
|
||||
|
||||
$diamonds = Cache::remember('leaderboard_diamonds', 60, fn () => UserCurrency::query()
|
||||
->whereNotIn('user_id', $this->staffIds)
|
||||
->where('type', 5)
|
||||
->orderByDesc('amount')
|
||||
->take(9)
|
||||
->with(['user' => fn ($q) => $q->select('id', 'username', 'look')])
|
||||
->get());
|
||||
|
||||
$mostOnline = Cache::remember('leaderboard_online', 60, fn () => $this->retrieveSettings('online_time'));
|
||||
|
||||
$respectsReceived = Cache::remember('leaderboard_respects', 60, fn () => $this->retrieveSettings('respects_received'));
|
||||
|
||||
$achievementScores = Cache::remember('leaderboard_achievements', 60, fn () => $this->retrieveSettings('achievement_score'));
|
||||
|
||||
return view('leaderboard', [
|
||||
'credits' => $topCredits,
|
||||
'duckets' => $duckets,
|
||||
'diamonds' => $diamonds,
|
||||
'mostOnline' => $mostOnline,
|
||||
'respectsReceived' => $respectsReceived,
|
||||
'achievementScores' => $achievementScores,
|
||||
]);
|
||||
}
|
||||
|
||||
private function retrieveSettings($column)
|
||||
{
|
||||
return UserSetting::query()
|
||||
->select(['user_id', $column])
|
||||
->whereNotIn('user_id', $this->staffIds)
|
||||
->orderByDesc($column)
|
||||
->take(9)
|
||||
->with('user:id,username,look')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Community\CameraService;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PhotosController extends Controller
|
||||
{
|
||||
public function __construct(private readonly CameraService $cameraService) {}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
return view('community.photos', [
|
||||
'photos' => $this->cameraService->fetchPhotos(true),
|
||||
]);
|
||||
}
|
||||
}
|
||||
+608
@@ -0,0 +1,608 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\RadioApplication;
|
||||
use App\Models\RadioBanner;
|
||||
use App\Models\RadioHistory;
|
||||
use App\Models\RadioRank;
|
||||
use App\Models\RadioSchedule;
|
||||
use App\Models\RadioShout;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RadioController extends Controller
|
||||
{
|
||||
private ?bool $azureCastDetected = null;
|
||||
|
||||
/** @var array<mixed> */
|
||||
private array $settingsCache = [];
|
||||
|
||||
/**
|
||||
* Get multiple settings at once (batched query)
|
||||
*
|
||||
* @param array<string> $keys
|
||||
*
|
||||
* @return array<string|null>
|
||||
*/
|
||||
private function getSettings(array $keys): array
|
||||
{
|
||||
$cacheKey = 'radio_settings_' . md5(implode(',', $keys));
|
||||
|
||||
return Cache::remember($cacheKey, 60, function () use ($keys): array {
|
||||
/** @var Collection $collection */
|
||||
$collection = WebsiteSetting::whereIn('key', $keys)
|
||||
->pluck('value', 'key');
|
||||
|
||||
return $collection->all();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single setting with caching
|
||||
*/
|
||||
private function getSetting(string $key, mixed $default = null): mixed
|
||||
{
|
||||
if (! isset($this->settingsCache[$key])) {
|
||||
$this->settingsCache[$key] = Cache::remember("setting_{$key}", 60, function () use ($key): mixed {
|
||||
/** @var WebsiteSetting|null $setting */
|
||||
$setting = WebsiteSetting::where('key', $key)->first();
|
||||
|
||||
return $setting?->value;
|
||||
});
|
||||
}
|
||||
|
||||
return $this->settingsCache[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
// Batch load all radio settings at once
|
||||
$settings = $this->getSettings([
|
||||
'radio_enabled',
|
||||
'radio_stream_url',
|
||||
'radio_current_dj_id',
|
||||
]);
|
||||
|
||||
if (! (bool) ($settings['radio_enabled'] ?? false)) {
|
||||
return view('community.radio.disabled');
|
||||
}
|
||||
|
||||
// Load all data with single queries each
|
||||
$ranks = Cache::remember('radio_ranks', 300, fn () => RadioRank::all());
|
||||
|
||||
$banners = Cache::remember('radio_banners_active', 60, fn () => RadioBanner::with('user:id,username,look')
|
||||
->active()
|
||||
->ordered()
|
||||
->take(10)
|
||||
->get());
|
||||
|
||||
$todaySchedule = Cache::remember('radio_schedule_today', 60, fn () => RadioSchedule::with('user:id,username,look')
|
||||
->active()
|
||||
->today()
|
||||
->orderBy('start_time')
|
||||
->get());
|
||||
|
||||
$currentDJ = $this->getCurrentDJFromSchedule();
|
||||
|
||||
$streamUrl = $this->formatStreamUrl($settings['radio_stream_url'] ?? '');
|
||||
|
||||
// Cache stream status check (30 seconds)
|
||||
$isOnline = Cache::remember('radio_stream_status', 30, fn () => $this->checkStreamOnline($streamUrl));
|
||||
|
||||
return view('community.radio.index', ['ranks' => $ranks, 'banners' => $banners, 'todaySchedule' => $todaySchedule, 'currentDJ' => $currentDJ, 'isOnline' => $isOnline, 'streamUrl' => $streamUrl]);
|
||||
}
|
||||
|
||||
public function rooster(): View
|
||||
{
|
||||
$schedule = Cache::remember('radio_schedule_all', 300, fn () => RadioSchedule::with('user:id,username,look')
|
||||
->active()
|
||||
->ordered()
|
||||
->get()
|
||||
->groupBy('day'));
|
||||
|
||||
return view('community.radio.rooster', ['schedule' => $schedule]);
|
||||
}
|
||||
|
||||
public function shouts(): RedirectResponse|View
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.shouts_disabled'));
|
||||
}
|
||||
|
||||
return view('community.radio.shouts');
|
||||
}
|
||||
|
||||
public function apply(): RedirectResponse|View
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_applications_enabled')) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.applications_closed'));
|
||||
}
|
||||
|
||||
$userId = auth()->id();
|
||||
|
||||
$hasPendingApplication = $userId && RadioApplication::where('user_id', $userId)
|
||||
->where('status', 'pending')
|
||||
->exists();
|
||||
|
||||
$ranks = Cache::remember('radio_ranks_accepting', 300, fn () => RadioRank::where('accepts_applications', true)->get());
|
||||
|
||||
return view('community.radio.apply', ['ranks' => $ranks, 'hasPendingApplication' => $hasPendingApplication]);
|
||||
}
|
||||
|
||||
public function storeApplication(Request $request): RedirectResponse
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_applications_enabled')) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.applications_closed'));
|
||||
}
|
||||
|
||||
/** @var int|string $userId */
|
||||
$userId = auth()->id();
|
||||
|
||||
$hasPendingApplication = RadioApplication::where('user_id', $userId)
|
||||
->where('status', 'pending')
|
||||
->exists();
|
||||
|
||||
if ($hasPendingApplication) {
|
||||
return back()->withErrors([
|
||||
'general' => __('radio.application_pending'),
|
||||
])->withInput();
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'real_name' => ['required', 'string', 'max:255'],
|
||||
'age' => ['required', 'integer', 'min:13', 'max:100'],
|
||||
'availability' => ['required', 'string', 'min:10'],
|
||||
'experience' => ['nullable', 'string'],
|
||||
'motivation' => ['required', 'string', 'min:50'],
|
||||
'music_style' => ['nullable', 'string', 'max:500'],
|
||||
'discord_username' => ['nullable', 'string', 'max:255'],
|
||||
'rank_id' => ['nullable', 'exists:radio_ranks,id'],
|
||||
]);
|
||||
|
||||
// Check rank accepts applications (cached)
|
||||
if ($validated['rank_id']) {
|
||||
$rank = Cache::remember("radio_rank_{$validated['rank_id']}", 300, fn () => RadioRank::find($validated['rank_id']));
|
||||
|
||||
if (! $rank || ! $rank->accepts_applications) {
|
||||
return back()->withErrors([
|
||||
'rank_id' => __('radio.rank_not_accepting', ['rank' => $rank?->name ?? 'Deze rank']),
|
||||
])->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
RadioApplication::create([
|
||||
'user_id' => $userId,
|
||||
'real_name' => $validated['real_name'],
|
||||
'age' => $validated['age'],
|
||||
'availability' => $validated['availability'],
|
||||
'experience' => $validated['experience'] ?? null,
|
||||
'motivation' => $validated['motivation'],
|
||||
'music_style' => $validated['music_style'] ?? null,
|
||||
'discord_username' => $validated['discord_username'] ?? null,
|
||||
'rank_id' => $validated['rank_id'] ?? null,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
return redirect()->route('radio.apply')->with('success', __('radio.application_sent'));
|
||||
}
|
||||
|
||||
public function storeShout(Request $request): RedirectResponse
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.shouts_disabled'));
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'message' => ['required', 'string', 'max:280'],
|
||||
]);
|
||||
|
||||
RadioShout::create([
|
||||
'user_id' => auth()->id(),
|
||||
'message' => $validated['message'],
|
||||
]);
|
||||
|
||||
// Clear shouts cache
|
||||
Cache::forget('radio_shouts_recent');
|
||||
|
||||
return redirect()->route('radio.shouts')->with('success', __('radio.shout_sent'));
|
||||
}
|
||||
|
||||
public function nowPlaying(): JsonResponse
|
||||
{
|
||||
// Cache now playing for 10 seconds to reduce API calls
|
||||
$nowPlaying = Cache::remember('radio_nowplaying', 10, fn () => $this->getNowPlaying());
|
||||
|
||||
return response()->json($nowPlaying);
|
||||
}
|
||||
|
||||
public function listeners(): JsonResponse
|
||||
{
|
||||
// Cache listeners count for 30 seconds
|
||||
$count = Cache::remember('radio_listeners', 30, fn () => $this->getListenersCount());
|
||||
|
||||
return response()->json(['count' => $count]);
|
||||
}
|
||||
|
||||
public function currentDJ(): JsonResponse
|
||||
{
|
||||
$dj = $this->getCurrentDJFromSchedule();
|
||||
|
||||
return response()->json([
|
||||
'dj' => $dj,
|
||||
'is_live' => $dj !== null,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getCurrentDJFromSchedule(): ?array
|
||||
{
|
||||
$manualDjId = $this->getSetting('radio_current_dj_id');
|
||||
|
||||
if (! empty($manualDjId)) {
|
||||
$dj = Cache::remember("user_{$manualDjId}", 60, fn () => User::find($manualDjId));
|
||||
|
||||
if ($dj) {
|
||||
return [
|
||||
'username' => $dj->username,
|
||||
'look' => $dj->look,
|
||||
'show_name' => 'Live DJ',
|
||||
'is_manual' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$currentSlot = RadioSchedule::with('user:id,username,look')
|
||||
->active()
|
||||
->where('day', $this->getCurrentDay())
|
||||
->whereTime('start_time', '<=', now()->format('H:i:s'))
|
||||
->whereTime('end_time', '>=', now()->format('H:i:s'))
|
||||
->first();
|
||||
|
||||
if ($currentSlot?->user) {
|
||||
return [
|
||||
'username' => $currentSlot->user->username,
|
||||
'look' => $currentSlot->user->look,
|
||||
'show_name' => $currentSlot->show_name,
|
||||
'start_time' => $currentSlot->start_time->format('H:i'),
|
||||
'end_time' => $currentSlot->end_time->format('H:i'),
|
||||
'is_manual' => false,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getNowPlaying(): array
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_now_playing_enabled')) {
|
||||
return ['enabled' => false, 'song' => null];
|
||||
}
|
||||
|
||||
$apiUrl = $this->getSetting('radio_now_playing_api_url') ?: $this->getAzureCastApiUrl();
|
||||
|
||||
if (! $apiUrl) {
|
||||
return ['enabled' => true, 'song' => null, 'artist' => null];
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::timeout(5)->get($apiUrl);
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
|
||||
if (isset($data['now_playing'])) {
|
||||
$song = $data['now_playing']['song'] ?? $data['now_playing'];
|
||||
|
||||
return [
|
||||
'enabled' => true,
|
||||
'song' => $song['title'] ?? $song['text'] ?? null,
|
||||
'artist' => $song['artist'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'enabled' => true,
|
||||
'song' => $data['song'] ?? $data['title'] ?? $data['now_playing'] ?? null,
|
||||
'artist' => $data['artist'] ?? null,
|
||||
];
|
||||
}
|
||||
} catch (\Exception) {
|
||||
// Silent fail
|
||||
}
|
||||
|
||||
return ['enabled' => true, 'song' => null, 'artist' => null];
|
||||
}
|
||||
|
||||
private function getAzureCastApiUrl(): ?string
|
||||
{
|
||||
$baseUrl = $this->getSetting('radio_azurecast_base_url');
|
||||
$stationId = $this->getSetting('radio_azurecast_station_id', '1');
|
||||
|
||||
if (! empty($baseUrl)) {
|
||||
return rtrim((string) $baseUrl, '/') . '/api/nowplaying/' . $stationId;
|
||||
}
|
||||
|
||||
$streamUrl = $this->getSetting('radio_stream_url');
|
||||
if (! $streamUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parsed = parse_url((string) $streamUrl);
|
||||
if (! $parsed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scheme = $parsed['scheme'] ?? 'https';
|
||||
$host = $parsed['host'] ?? '';
|
||||
|
||||
return $scheme . '://' . $host . '/api/nowplaying/' . $stationId;
|
||||
}
|
||||
|
||||
private function getListenersCount(): int
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_listeners_enabled')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$apiUrl = $this->getSetting('radio_listeners_api_url') ?: $this->getAzureCastApiUrl();
|
||||
|
||||
if (! $apiUrl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::timeout(5)->get($apiUrl);
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
|
||||
return $data['listeners']['total']
|
||||
?? $data['listeners']['current']
|
||||
?? $data['listeners']
|
||||
?? $data['count']
|
||||
?? $data['total']
|
||||
?? 0;
|
||||
}
|
||||
} catch (\Exception) {
|
||||
// Silent fail
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function getCurrentDay(): string
|
||||
{
|
||||
return strtolower(now()->format('l'));
|
||||
}
|
||||
|
||||
private function formatStreamUrl(string $url): string
|
||||
{
|
||||
if ($url === '' || $url === '0') {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$url = str_replace('http://', 'https://', $url);
|
||||
|
||||
if (preg_match('/^(https?:\/\/[^\/]+):(\d+)\/(.+)$/', $url, $matches)) {
|
||||
$baseUrl = $matches[1];
|
||||
$port = $matches[2];
|
||||
$path = $matches[3];
|
||||
|
||||
if (in_array($port, ['8000', '8010', '8020', '8030', '8040', '8050'])) {
|
||||
return $baseUrl . '/radio/' . $port . '/' . $path;
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
private function checkStreamOnline(string $streamUrl): bool
|
||||
{
|
||||
if ($streamUrl === '' || $streamUrl === '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return Http::timeout(2)->withOptions(['verify' => false])->head($streamUrl)->successful();
|
||||
} catch (\Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function detectAzureCast(): bool
|
||||
{
|
||||
if ($this->azureCastDetected !== null) {
|
||||
return $this->azureCastDetected;
|
||||
}
|
||||
|
||||
$baseUrl = $this->getSetting('radio_azurecast_base_url');
|
||||
if (! empty($baseUrl)) {
|
||||
$this->azureCastDetected = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$streamUrl = $this->getSetting('radio_stream_url', '');
|
||||
if (empty($streamUrl)) {
|
||||
$this->azureCastDetected = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$parsed = parse_url((string) $streamUrl);
|
||||
if (! $parsed) {
|
||||
$this->azureCastDetected = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$scheme = $parsed['scheme'] ?? 'https';
|
||||
$host = $parsed['host'] ?? '';
|
||||
$testUrl = $scheme . '://' . $host . '/api/nowplaying';
|
||||
|
||||
try {
|
||||
$response = Http::timeout(3)->get($testUrl);
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
if (is_array($data) && (isset($data[0]['station']) || isset($data['station']))) {
|
||||
$this->azureCastDetected = true;
|
||||
|
||||
$stationId = $data[0]['station']['id'] ?? $data['station']['id'] ?? 1;
|
||||
$detectedBaseUrl = $scheme . '://' . $host;
|
||||
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_azurecast_base_url'],
|
||||
['value' => $detectedBaseUrl, 'comment' => 'Auto-detected AzureCast'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_azurecast_station_id'],
|
||||
['value' => $stationId, 'comment' => 'Auto-detected Station ID'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_now_playing_enabled'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_listeners_enabled'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_show_current_dj'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_widget_enabled'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_widget_show_globally'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
|
||||
$this->settingsCache = [];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception) {
|
||||
// Not AzureCast
|
||||
}
|
||||
|
||||
$this->azureCastDetected = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function config(): JsonResponse
|
||||
{
|
||||
$settings = $this->getSettings([
|
||||
'radio_enabled',
|
||||
'radio_stream_url',
|
||||
'radio_style',
|
||||
'radio_now_playing_enabled',
|
||||
'radio_listeners_enabled',
|
||||
'radio_show_current_dj',
|
||||
'radio_widget_enabled',
|
||||
'radio_widget_show_globally',
|
||||
'radio_widget_position',
|
||||
]);
|
||||
|
||||
$streamUrl = $this->formatStreamUrl($settings['radio_stream_url'] ?? '');
|
||||
$isAzurecast = $this->detectAzureCast();
|
||||
|
||||
return response()->json([
|
||||
'enabled' => (bool) ($settings['radio_enabled'] ?? false),
|
||||
'stream_url' => $streamUrl,
|
||||
'style' => $settings['radio_style'] ?? 'dark',
|
||||
'dj' => $this->getCurrentDJFromSchedule(),
|
||||
'now_playing_enabled' => (bool) ($settings['radio_now_playing_enabled'] ?? false),
|
||||
'listeners_enabled' => (bool) ($settings['radio_listeners_enabled'] ?? false),
|
||||
'show_current_dj' => (bool) ($settings['radio_show_current_dj'] ?? false),
|
||||
'widget_enabled' => (bool) ($settings['radio_widget_enabled'] ?? false),
|
||||
'widget_show_globally' => (bool) ($settings['radio_widget_show_globally'] ?? false),
|
||||
'widget_position' => $settings['radio_widget_position'] ?? 'bottom-right',
|
||||
'is_azurecast' => $isAzurecast,
|
||||
'azurecast_detected' => $isAzurecast,
|
||||
]);
|
||||
}
|
||||
|
||||
public function startSession(Request $request): JsonResponse
|
||||
{
|
||||
$userId = auth()->id();
|
||||
|
||||
$activeSession = RadioHistory::where('user_id', $userId)
|
||||
->whereNull('ended_at')
|
||||
->first();
|
||||
|
||||
if ($activeSession) {
|
||||
return response()->json([
|
||||
'error' => 'Je hebt al een actieve sessie',
|
||||
'session_id' => $activeSession->id,
|
||||
], 400);
|
||||
}
|
||||
|
||||
$session = RadioHistory::create([
|
||||
'user_id' => $userId,
|
||||
'show_name' => $request->input('show_name'),
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Sessie gestart',
|
||||
'session_id' => $session->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function endSession(): JsonResponse
|
||||
{
|
||||
$userId = auth()->id();
|
||||
|
||||
$activeSession = RadioHistory::where('user_id', $userId)
|
||||
->whereNull('ended_at')
|
||||
->first();
|
||||
|
||||
if (! $activeSession) {
|
||||
return response()->json([
|
||||
'error' => 'Geen actieve sessie gevonden',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$activeSession->endSession();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Sessie beëindigd',
|
||||
'duration' => $activeSession->duration,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getShouts(): JsonResponse
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
|
||||
return response()->json([
|
||||
'error' => 'Shouts zijn uitgeschakeld',
|
||||
'shouts' => [],
|
||||
], 403);
|
||||
}
|
||||
|
||||
$shouts = Cache::remember('radio_shouts_recent', 30, fn () => RadioShout::with('user:id,username')
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(50)
|
||||
->get()
|
||||
->map(fn ($shout) => [
|
||||
'id' => $shout->id,
|
||||
'username' => $shout->user?->username ?? 'Anoniem',
|
||||
'message' => $shout->message,
|
||||
'created_at' => $shout->created_at->diffForHumans(),
|
||||
]));
|
||||
|
||||
return response()->json([
|
||||
'shouts' => $shouts,
|
||||
'total' => $shouts->count(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Community;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RadioLeaderboardController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$period = $request->get('period', 'all');
|
||||
|
||||
$users = match ($period) {
|
||||
'weekly' => $this->getWeeklyLeaderboard(),
|
||||
'monthly' => $this->getMonthlyLeaderboard(),
|
||||
default => $this->getAllTimeLeaderboard(),
|
||||
};
|
||||
|
||||
// Extract top 3 from already-fetched data
|
||||
$topUserData = array_slice($users, 0, 3);
|
||||
$topUserIds = array_column($topUserData, 'id');
|
||||
$topUsers = User::whereIn('id', $topUserIds)
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
return view('community.radio.leaderboard', ['users' => $users, 'topUsers' => $topUsers, 'period' => $period]);
|
||||
}
|
||||
|
||||
private function getAllTimeLeaderboard(): array
|
||||
{
|
||||
return User::where('radio_points', '>', 0)
|
||||
->orderBy('radio_points', 'desc')
|
||||
->limit(100)
|
||||
->get()
|
||||
->map(fn ($user, $index) => [
|
||||
'rank' => $index + 1,
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'avatar' => $user->avatar ?? null,
|
||||
'points' => $user->radio_points,
|
||||
])
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private function getWeeklyLeaderboard(): array
|
||||
{
|
||||
return $this->getPointsLeaderboard(now()->subWeek());
|
||||
}
|
||||
|
||||
private function getMonthlyLeaderboard(): array
|
||||
{
|
||||
return $this->getPointsLeaderboard(now()->subMonth());
|
||||
}
|
||||
|
||||
private function getPointsLeaderboard(Carbon $since): array
|
||||
{
|
||||
$points = DB::table('radio_listener_points')
|
||||
->where('earned_at', '>=', $since)
|
||||
->select('user_id', DB::raw('SUM(points) as total_points'))
|
||||
->groupBy('user_id')
|
||||
->orderBy('total_points', 'desc')
|
||||
->limit(100)
|
||||
->get();
|
||||
|
||||
$userIds = $points->pluck('user_id')->toArray();
|
||||
$users = User::whereIn('id', $userIds)->get()->keyBy('id');
|
||||
|
||||
return $points->map(function ($item, $index) use ($users) {
|
||||
$user = $users->get($item->user_id);
|
||||
|
||||
return [
|
||||
'rank' => $index + 1,
|
||||
'id' => $item->user_id,
|
||||
'username' => $user?->username ?? 'Onbekend',
|
||||
'avatar' => $user?->avatar ?? null,
|
||||
'points' => (int) $item->total_points,
|
||||
];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community\Staff;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Community\Staff\WebsiteOpenPosition;
|
||||
use App\Models\Community\Staff\WebsiteStaffApplications;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class StaffApplicationsController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$positions = WebsiteOpenPosition::query()
|
||||
->where('position_kind', 'rank')
|
||||
->whereNotNull('permission_id')
|
||||
->with('permission')
|
||||
->whereHas('permission')
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return view('community.staff-applications', ['positions' => $positions]);
|
||||
}
|
||||
|
||||
public function show(WebsiteOpenPosition $position): View
|
||||
{
|
||||
abort_unless($position->position_kind === 'rank', 404);
|
||||
$position->loadMissing('permission');
|
||||
abort_unless($position->permission, 404);
|
||||
|
||||
return view('community.staff-apply', ['position' => $position]);
|
||||
}
|
||||
|
||||
public function store(Request $request, WebsiteOpenPosition $position)
|
||||
{
|
||||
abort_unless($position->position_kind === 'rank', 404);
|
||||
|
||||
$validated = $request->validate([
|
||||
'content' => ['required', 'string', 'min:10'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
if ($user->hasAppliedForPosition($position->permission_id)) {
|
||||
return back()->withErrors([
|
||||
'content' => __('You have already applied for this position.'),
|
||||
])->withInput();
|
||||
}
|
||||
|
||||
WebsiteStaffApplications::create([
|
||||
'user_id' => $user->id,
|
||||
'rank_id' => $position->permission_id,
|
||||
'content' => $validated['content'],
|
||||
]);
|
||||
|
||||
return redirect()
|
||||
->route('staff-applications.index')
|
||||
->with('status', __('Your application has been submitted!'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community\Staff;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Community\StaffService;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class StaffController extends Controller
|
||||
{
|
||||
public function __construct(private readonly StaffService $staffService) {}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
$employees = $this->staffService->fetchStaffPositions();
|
||||
|
||||
return view('community.staff', [
|
||||
'employees' => $employees,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community\Staff;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Community\Staff\WebsiteOpenPosition;
|
||||
use App\Models\Community\Staff\WebsiteStaffApplications;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class WebsiteTeamApplicationsController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$positions = WebsiteOpenPosition::query()
|
||||
->where('position_kind', 'team')
|
||||
->whereNotNull('team_id')
|
||||
->with('team')
|
||||
->whereHas('team')
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$userAppStatuses = [];
|
||||
if (auth()->check()) {
|
||||
$teamIds = $positions->pluck('team_id')->filter()->unique()->all();
|
||||
|
||||
$userAppStatuses = WebsiteStaffApplications::query()
|
||||
->where('user_id', auth()->id())
|
||||
->whereIn('team_id', $teamIds)
|
||||
->pluck('status', 'team_id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return view('community.team-applications', [
|
||||
'positions' => $positions,
|
||||
'userAppStatuses' => $userAppStatuses,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(WebsiteOpenPosition $position): View
|
||||
{
|
||||
abort_unless($position->position_kind === 'team', 404);
|
||||
|
||||
$position->loadMissing('team');
|
||||
|
||||
return view('community.team-apply', [
|
||||
'position' => $position,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request, WebsiteOpenPosition $position)
|
||||
{
|
||||
abort_unless($position->position_kind === 'team', 404);
|
||||
|
||||
$request->validate([
|
||||
'content' => ['required', 'string', 'min:10'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
if ($user->hasAppliedForTeam($position->team_id)) {
|
||||
return back()->withErrors([
|
||||
'content' => __('You have already applied for this team.'),
|
||||
]);
|
||||
}
|
||||
|
||||
WebsiteStaffApplications::create([
|
||||
'user_id' => $user->id,
|
||||
'team_id' => $position->team_id,
|
||||
'content' => $request->string('content'),
|
||||
]);
|
||||
|
||||
return redirect()
|
||||
->route('team-applications.index')
|
||||
->with('status', __('Your application has been submitted!'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community\Staff;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Community\TeamService;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class WebsiteTeamsController extends Controller
|
||||
{
|
||||
public function __construct(private readonly TeamService $teamService) {}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
$employees = $this->teamService->fetchTeams();
|
||||
|
||||
return view('community.teams', [
|
||||
'employees' => $employees,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Community;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\RareSearchFormRequest;
|
||||
use App\Models\Community\RareValue\WebsiteRareValue;
|
||||
use App\Models\Community\RareValue\WebsiteRareValueCategory;
|
||||
use App\Models\Game\Furniture\Item;
|
||||
use App\Services\Community\RareValues\RareValueCategoriesService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class WebsiteRareValuesController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RareValueCategoriesService $valueCategoriesService,
|
||||
) {}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
return view('rare-values', [
|
||||
'categories' => $this->valueCategoriesService->fetchCategoriesByPriority(),
|
||||
'categoriesNav' => $this->valueCategoriesService->fetchAllCategories(),
|
||||
'statistics' => $this->valueCategoriesService->getRareStatistics(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function category(int $id, Request $request): View|RedirectResponse
|
||||
{
|
||||
$category = $this->valueCategoriesService->fetchCategoryById($id);
|
||||
|
||||
if (! $category instanceof WebsiteRareValueCategory) {
|
||||
return redirect()->back()->withErrors([
|
||||
'message' => __('The entered category does not exist'),
|
||||
]);
|
||||
}
|
||||
|
||||
// Get sort parameters
|
||||
$sortBy = $request->get('sort', 'name');
|
||||
$sortOrder = $request->get('order', 'asc');
|
||||
|
||||
// Validate sort parameters
|
||||
$allowedSorts = ['name', 'credit_value', 'currency_value', 'created_at'];
|
||||
$allowedOrders = ['asc', 'desc'];
|
||||
|
||||
if (! in_array($sortBy, $allowedSorts)) {
|
||||
$sortBy = 'name';
|
||||
}
|
||||
|
||||
if (! in_array($sortOrder, $allowedOrders)) {
|
||||
$sortOrder = 'asc';
|
||||
}
|
||||
|
||||
return view('rare-values', [
|
||||
'category' => $category,
|
||||
'categoriesNav' => $this->valueCategoriesService->fetchAllCategories(),
|
||||
'statistics' => $this->valueCategoriesService->getRareStatistics(),
|
||||
'sortBy' => $sortBy,
|
||||
'sortOrder' => $sortOrder,
|
||||
]);
|
||||
}
|
||||
|
||||
public function search(RareSearchFormRequest $request): View|RedirectResponse
|
||||
{
|
||||
$searchTerm = $request->input('search');
|
||||
|
||||
$categories = $this->valueCategoriesService->searchCategories($searchTerm);
|
||||
|
||||
if ($categories->isEmpty()) {
|
||||
return redirect()->back()->withErrors([
|
||||
'message' => __('It seems like there were no rares matching your search input'),
|
||||
]);
|
||||
}
|
||||
|
||||
return view('rare-values', [
|
||||
'categories' => $categories,
|
||||
'categoriesNav' => $this->valueCategoriesService->fetchAllCategories(),
|
||||
'statistics' => $this->valueCategoriesService->getRareStatistics(),
|
||||
'searchTerm' => $searchTerm,
|
||||
]);
|
||||
}
|
||||
|
||||
public function value(WebsiteRareValue $value): View
|
||||
{
|
||||
$cacheKey = "rare_value_items_{$value->id}";
|
||||
|
||||
$items = Cache::remember($cacheKey, 300, fn () => Item::with(['user:id,username,look'])
|
||||
->where('item_id', $value->item_id)
|
||||
->get());
|
||||
|
||||
$itemsPerUser = $items->groupBy('user_id')->map(fn ($group) => [
|
||||
'user' => $group->first()->user,
|
||||
'item_count' => $group->count(),
|
||||
]);
|
||||
|
||||
// Get total count
|
||||
$totalItems = $items->count();
|
||||
|
||||
return view('value', [
|
||||
'value' => $value,
|
||||
'items' => $itemsPerUser,
|
||||
'totalItems' => $totalItems,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user