You've already forked Atomcms-edit
Initial commit
This commit is contained in:
+175
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\RadioRank;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RadioSetupController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
return view('admin.radio.setup');
|
||||
}
|
||||
|
||||
public function setup(Request $request): RedirectResponse
|
||||
{
|
||||
try {
|
||||
// Standard radio settings
|
||||
$settings = [
|
||||
// Basic Radio Settings
|
||||
'radio_enabled' => '1',
|
||||
'radio_stream_url' => 'https://stream.radioking.com/radio/83232/radio.mp3',
|
||||
'radio_style' => 'dark',
|
||||
'radio_auto_play' => '0',
|
||||
|
||||
// Points System Settings
|
||||
'points_enabled' => '1',
|
||||
'points_per_minute' => '2',
|
||||
'max_points_per_day' => '100',
|
||||
'points_for_request' => '5',
|
||||
'points_for_vote' => '2',
|
||||
'points_for_giveaway_win' => '50',
|
||||
'points_for_contest_win' => '100',
|
||||
|
||||
// Features Settings
|
||||
'radio_shouts_enabled' => '1',
|
||||
'radio_now_playing_enabled' => '1',
|
||||
'radio_listeners_enabled' => '1',
|
||||
'radio_show_current_dj' => '1',
|
||||
'radio_widget_enabled' => '1',
|
||||
'radio_widget_show_globally' => '1',
|
||||
'radio_widget_position' => 'bottom-right',
|
||||
|
||||
// DJ Settings
|
||||
'radio_applications_enabled' => '1',
|
||||
'radio_auto_dj_detection' => '1',
|
||||
|
||||
// Monitoring Settings
|
||||
'radio_monitoring_enabled' => '1',
|
||||
'radio_monitoring_timeout' => '5',
|
||||
|
||||
// Stream Settings
|
||||
'radio_stream_fallback_url' => '',
|
||||
'radio_stream_backup_enabled' => '0',
|
||||
|
||||
// Display Settings
|
||||
'radio_show_song_history' => '1',
|
||||
'radio_show_schedule_preview' => '1',
|
||||
'radio_max_history_items' => '10',
|
||||
|
||||
// Social Settings
|
||||
'radio_social_links_enabled' => '1',
|
||||
'radio_facebook_url' => '',
|
||||
'radio_twitter_url' => '',
|
||||
'radio_discord_url' => '',
|
||||
'radio_instagram_url' => '',
|
||||
'radio_youtube_url' => '',
|
||||
'radio_twitch_url' => '',
|
||||
|
||||
// Moderation Settings
|
||||
'radio_word_filter_enabled' => '1',
|
||||
'radio_max_shout_length' => '280',
|
||||
'radio_shout_cooldown' => '30',
|
||||
|
||||
// Contest Settings
|
||||
'radio_contests_enabled' => '1',
|
||||
'radio_giveaways_enabled' => '1',
|
||||
'radio_auto_contest_creation' => '0',
|
||||
];
|
||||
|
||||
// Update all settings
|
||||
foreach ($settings as $key => $value) {
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => $key],
|
||||
['value' => $value, 'comment' => $this->getSettingComment($key)],
|
||||
);
|
||||
}
|
||||
|
||||
// Create default radio ranks if they don't exist
|
||||
$this->createDefaultRanks();
|
||||
|
||||
// Clear caches
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
|
||||
return redirect()->route('admin.radio.setup')
|
||||
->with('success', __('radio.setup.success_body'));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('admin.radio.setup')
|
||||
->with('error', __('radio.setup.error_body', ['message' => $e->getMessage()]));
|
||||
}
|
||||
}
|
||||
|
||||
private function createDefaultRanks(): void
|
||||
{
|
||||
$ranks = [
|
||||
['name' => 'Trainee DJ', 'level' => 1, 'is_active' => true, 'description' => 'Beginnende DJ'],
|
||||
['name' => 'Junior DJ', 'level' => 2, 'is_active' => true, 'description' => 'Ervaren DJ'],
|
||||
['name' => 'Senior DJ', 'level' => 3, 'is_active' => true, 'description' => 'Professionele DJ'],
|
||||
['name' => 'Head DJ', 'level' => 4, 'is_active' => true, 'description' => 'Hoofd DJ'],
|
||||
['name' => 'Radio Manager', 'level' => 5, 'is_active' => true, 'description' => 'Radio Manager'],
|
||||
];
|
||||
|
||||
foreach ($ranks as $rank) {
|
||||
RadioRank::updateOrCreate(
|
||||
['name' => $rank['name']],
|
||||
$rank,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getSettingComment(string $key): string
|
||||
{
|
||||
$comments = [
|
||||
'radio_enabled' => 'Enable radio system',
|
||||
'radio_stream_url' => 'Main radio stream URL',
|
||||
'radio_style' => 'Radio player theme style',
|
||||
'radio_auto_play' => 'Auto-play radio on page load',
|
||||
'points_enabled' => 'Enable points system',
|
||||
'points_per_minute' => 'Points awarded per minute',
|
||||
'max_points_per_day' => 'Maximum points per day',
|
||||
'points_for_request' => 'Points for song request',
|
||||
'points_for_vote' => 'Points for voting',
|
||||
'points_for_giveaway_win' => 'Points for giveaway win',
|
||||
'points_for_contest_win' => 'Points for contest win',
|
||||
'radio_shouts_enabled' => 'Enable shouts system',
|
||||
'radio_now_playing_enabled' => 'Show now playing info',
|
||||
'radio_listeners_enabled' => 'Show listener count',
|
||||
'radio_show_current_dj' => 'Show current DJ',
|
||||
'radio_widget_enabled' => 'Enable radio widget',
|
||||
'radio_widget_show_globally' => 'Show widget on all pages',
|
||||
'radio_widget_position' => 'Widget position',
|
||||
'radio_applications_enabled' => 'Enable DJ applications',
|
||||
'radio_auto_dj_detection' => 'Auto-detect DJ from schedule',
|
||||
'radio_monitoring_enabled' => 'Enable stream monitoring',
|
||||
'radio_monitoring_timeout' => 'Stream check timeout',
|
||||
'radio_stream_fallback_url' => 'Backup stream URL',
|
||||
'radio_stream_backup_enabled' => 'Enable backup stream',
|
||||
'radio_show_song_history' => 'Show recent songs',
|
||||
'radio_show_schedule_preview' => 'Show today\'s schedule',
|
||||
'radio_max_history_items' => 'Max history items to show',
|
||||
'radio_social_links_enabled' => 'Enable social media links',
|
||||
'radio_facebook_url' => 'Facebook page URL',
|
||||
'radio_twitter_url' => 'Twitter profile URL',
|
||||
'radio_discord_url' => 'Discord server URL',
|
||||
'radio_instagram_url' => 'Instagram profile URL',
|
||||
'radio_youtube_url' => 'YouTube channel URL',
|
||||
'radio_twitch_url' => 'Twitch channel URL',
|
||||
'radio_word_filter_enabled' => 'Enable word filter',
|
||||
'radio_max_shout_length' => 'Maximum shout character length',
|
||||
'radio_shout_cooldown' => 'Shout cooldown in seconds',
|
||||
'radio_contests_enabled' => 'Enable contests',
|
||||
'radio_giveaways_enabled' => 'Enable giveaways',
|
||||
'radio_auto_contest_creation' => 'Auto-create contests',
|
||||
];
|
||||
|
||||
return $comments[$key] ?? 'Radio setting';
|
||||
}
|
||||
}
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Fortify\CreateNewUser;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Articles\WebsiteArticle;
|
||||
use App\Models\Miscellaneous\CameraWeb;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function login(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'username' => ['required', 'string'],
|
||||
'password' => ['required'],
|
||||
]);
|
||||
|
||||
$username = $request->input('username');
|
||||
$user = User::where('username', $username)
|
||||
->orWhere('mail', $username)
|
||||
->first();
|
||||
|
||||
if (! $user || ! Hash::check($request->input('password'), $user->password)) {
|
||||
throw ValidationException::withMessages([
|
||||
'email' => ['The provided credentials are incorrect.'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($user->is_banned) {
|
||||
throw ValidationException::withMessages([
|
||||
'email' => ['Your account has been banned.'],
|
||||
]);
|
||||
}
|
||||
|
||||
$user->update(['last_login' => time()]);
|
||||
|
||||
$token = $user->createToken('auth-token')->plainTextToken;
|
||||
|
||||
return response()->json([
|
||||
'user' => [
|
||||
'id' => (string) $user->id,
|
||||
'email' => $user->mail,
|
||||
'username' => $user->username,
|
||||
'look' => $user->look,
|
||||
],
|
||||
'token' => $token,
|
||||
]);
|
||||
}
|
||||
|
||||
public function register(Request $request): JsonResponse
|
||||
{
|
||||
$createNewUser = new CreateNewUser;
|
||||
|
||||
try {
|
||||
$validated = $request->validate([
|
||||
'username' => ['required', 'string', 'max:50'],
|
||||
'password' => ['required', 'string', 'min:6'],
|
||||
'mail' => ['required', 'email', 'max:255'],
|
||||
'look' => ['nullable', 'string'],
|
||||
'motto' => ['nullable', 'string', 'max:100'],
|
||||
]);
|
||||
|
||||
$user = $createNewUser->create($validated);
|
||||
|
||||
$token = $user->createToken('auth-token')->plainTextToken;
|
||||
|
||||
return response()->json([
|
||||
'user' => [
|
||||
'id' => (string) $user->id,
|
||||
'email' => $user->mail,
|
||||
'username' => $user->username,
|
||||
'look' => $user->look,
|
||||
],
|
||||
'token' => $token,
|
||||
], 201);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json([
|
||||
'errors' => $e->errors(),
|
||||
], 422);
|
||||
}
|
||||
}
|
||||
|
||||
public function user(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
return response()->json([
|
||||
'id' => (string) $user->id,
|
||||
'email' => $user->mail,
|
||||
'username' => $user->username,
|
||||
'look' => $user->look,
|
||||
'motto' => $user->motto ?? '',
|
||||
'credits' => $user->credits ?? 0,
|
||||
'pixels' => $user->pixels ?? 0,
|
||||
'diamonds' => $user->diamonds ?? 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout(Request $request): JsonResponse
|
||||
{
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
|
||||
return response()->json(['message' => 'Logged out successfully']);
|
||||
}
|
||||
|
||||
public function home(): JsonResponse
|
||||
{
|
||||
$articles = WebsiteArticle::with(['user:id,username,look'])
|
||||
->latest('id')
|
||||
->take(4)
|
||||
->get()
|
||||
->map(fn ($article) => [
|
||||
'id' => $article->id,
|
||||
'title' => $article->title,
|
||||
'slug' => $article->slug,
|
||||
'image' => $article->image,
|
||||
'excerpt' => $article->excerpt,
|
||||
'user' => $article->user,
|
||||
'created_at' => $article->created_at,
|
||||
]);
|
||||
|
||||
$photos = CameraWeb::query()
|
||||
->latest('id')
|
||||
->take(4)
|
||||
->where('visible', true)
|
||||
->with('user:id,username,look')
|
||||
->get()
|
||||
->map(fn ($photo) => [
|
||||
'id' => $photo->id,
|
||||
'image' => $photo->image,
|
||||
'user' => $photo->user,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'articles' => $articles,
|
||||
'photos' => $photos,
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateUser(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'motto' => ['nullable', 'string', 'max:100'],
|
||||
'look' => ['nullable', 'string'],
|
||||
]);
|
||||
|
||||
$user->update($validated);
|
||||
|
||||
return response()->json([
|
||||
'id' => (string) $user->id,
|
||||
'email' => $user->mail,
|
||||
'username' => $user->username,
|
||||
'look' => $user->look,
|
||||
'motto' => $user->motto,
|
||||
'credits' => $user->credits,
|
||||
'pixels' => $user->pixels,
|
||||
'diamonds' => $user->diamonds,
|
||||
]);
|
||||
}
|
||||
|
||||
public function articleComment(Request $request, string $slug): JsonResponse
|
||||
{
|
||||
$article = WebsiteArticle::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$validated = $request->validate([
|
||||
'comment' => ['required', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$comment = $article->comments()->create([
|
||||
'user_id' => $request->user()->id,
|
||||
'comment' => strip_tags((string) $validated['comment']),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'data' => $comment->load('user:id,username,look'),
|
||||
], 201);
|
||||
}
|
||||
}
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class FurniEditorController extends Controller
|
||||
{
|
||||
private function checkAdmin(): void
|
||||
{
|
||||
if (! Auth::check() || Auth::user()->rank < (int) setting('min_staff_rank', 7)) {
|
||||
abort(403, 'Forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
public function search(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$q = mb_substr(strip_tags((string) $request->input('q', '')), 0, 100);
|
||||
$type = $request->input('type', '');
|
||||
$page = max(1, (int) $request->input('page', 1));
|
||||
$limit = min(50, max(1, (int) $request->input('limit', 20)));
|
||||
|
||||
$query = DB::table('items_base');
|
||||
|
||||
if ($q !== '' && $q !== '0') {
|
||||
$query->where(function ($w) use ($q) {
|
||||
$w->where('item_name', 'like', "%{$q}%")
|
||||
->orWhere('public_name', 'like', "%{$q}%")
|
||||
->orWhere('id', '=', $q);
|
||||
});
|
||||
}
|
||||
|
||||
if ($type) {
|
||||
$query->where('type', $type);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
|
||||
$items = $query
|
||||
->orderBy('id')
|
||||
->skip(($page - 1) * $limit)
|
||||
->take($limit)
|
||||
->get()
|
||||
->map(fn ($r) => [
|
||||
'id' => $r->id,
|
||||
'spriteId' => $r->sprite_id,
|
||||
'itemName' => $r->item_name,
|
||||
'publicName' => $r->public_name,
|
||||
'type' => $r->type,
|
||||
'width' => $r->width,
|
||||
'length' => $r->length,
|
||||
'stackHeight' => $r->stack_height,
|
||||
'allowStack' => (bool) $r->allow_stack,
|
||||
'allowWalk' => (bool) $r->allow_walk,
|
||||
'allowSit' => (bool) $r->allow_sit,
|
||||
'allowLay' => (bool) $r->allow_lay,
|
||||
'interactionType' => $r->interaction_type,
|
||||
'interactionModesCount' => $r->interaction_modes_count,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
]);
|
||||
} catch (\Exception) {
|
||||
Log::error('[FurniEditor] Search failed', ['error' => 'Actie mislukt']);
|
||||
|
||||
return response()->json(['error' => 'Zoeken mislukt'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function detail(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$id = (int) $request->input('id');
|
||||
if ($id === 0) {
|
||||
return response()->json(['error' => 'Missing id'], 400);
|
||||
}
|
||||
|
||||
$item = DB::table('items_base')->where('id', $id)->first();
|
||||
if (! $item) {
|
||||
return response()->json(['error' => 'Item not found'], 404);
|
||||
}
|
||||
|
||||
$catalog = DB::table('catalog_items')
|
||||
->whereRaw('FIND_IN_SET(?, item_ids)', [$id])
|
||||
->get()
|
||||
->map(fn ($r) => [
|
||||
'id' => $r->id,
|
||||
'catalogName' => $r->catalog_name,
|
||||
'costCredits' => $r->cost_credits,
|
||||
'costPoints' => $r->cost_points,
|
||||
'pointsType' => $r->points_type,
|
||||
'pageId' => $r->page_id,
|
||||
'pageName' => $r->page_id ? DB::table('catalog_pages')->where('id', $r->page_id)->value('caption') ?? '' : '',
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'item' => [
|
||||
'id' => $item->id,
|
||||
'spriteId' => $item->sprite_id,
|
||||
'itemName' => $item->item_name,
|
||||
'publicName' => $item->public_name,
|
||||
'type' => $item->type,
|
||||
'width' => $item->width,
|
||||
'length' => $item->length,
|
||||
'stackHeight' => $item->stack_height,
|
||||
'allowStack' => (bool) $item->allow_stack,
|
||||
'allowWalk' => (bool) $item->allow_walk,
|
||||
'allowSit' => (bool) $item->allow_sit,
|
||||
'allowLay' => (bool) $item->allow_lay,
|
||||
'allowGift' => (bool) $item->allow_gift,
|
||||
'allowTrade' => (bool) $item->allow_trade,
|
||||
'allowRecycle' => (bool) $item->allow_recycle,
|
||||
'allowMarketplaceSell' => (bool) $item->allow_marketplace_sell,
|
||||
'allowInventoryStack' => (bool) $item->allow_inventory_stack,
|
||||
'interactionType' => $item->interaction_type,
|
||||
'interactionModesCount' => $item->interaction_modes_count,
|
||||
'vendingIds' => $item->vending_ids,
|
||||
'multiheight' => $item->multiheight,
|
||||
'customparams' => $item->customparams,
|
||||
'effectIdMale' => $item->effect_id_male,
|
||||
'effectIdFemale' => $item->effect_id_female,
|
||||
'clothingOnWalk' => $item->clothing_on_walk,
|
||||
'description' => '',
|
||||
'usageCount' => 0,
|
||||
],
|
||||
'catalogItems' => $catalog,
|
||||
'furniDataEntry' => null,
|
||||
]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Kan item niet laden'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$id = (int) $request->input('id');
|
||||
if ($id === 0) {
|
||||
return response()->json(['error' => 'Missing id'], 400);
|
||||
}
|
||||
|
||||
$data = $request->json()->all();
|
||||
$update = [];
|
||||
|
||||
$map = [
|
||||
'itemName' => ['item_name', 'string', 100],
|
||||
'publicName' => ['public_name', 'string', 100],
|
||||
'spriteId' => ['sprite_id', 'integer', 0],
|
||||
'width' => ['width', 'integer', 0],
|
||||
'length' => ['length', 'integer', 0],
|
||||
'stackHeight' => ['stack_height', 'integer', 0],
|
||||
'allowStack' => ['allow_stack', 'boolean', 0],
|
||||
'allowWalk' => ['allow_walk', 'boolean', 0],
|
||||
'allowSit' => ['allow_sit', 'boolean', 0],
|
||||
'allowLay' => ['allow_lay', 'boolean', 0],
|
||||
'allowGift' => ['allow_gift', 'boolean', 0],
|
||||
'allowTrade' => ['allow_trade', 'boolean', 0],
|
||||
'allowRecycle' => ['allow_recycle', 'boolean', 0],
|
||||
'allowMarketplaceSell' => ['allow_marketplace_sell', 'boolean', 0],
|
||||
'allowInventoryStack' => ['allow_inventory_stack', 'boolean', 0],
|
||||
'interactionType' => ['interaction_type', 'string', 50],
|
||||
'interactionModesCount' => ['interaction_modes_count', 'integer', 0],
|
||||
'vendingIds' => ['vending_ids', 'string', 255],
|
||||
'multiheight' => ['multiheight', 'string', 255],
|
||||
'customparams' => ['customparams', 'string', 255],
|
||||
'effectIdMale' => ['effect_id_male', 'integer', 0],
|
||||
'effectIdFemale' => ['effect_id_female', 'integer', 0],
|
||||
'clothingOnWalk' => ['clothing_on_walk', 'string', 255],
|
||||
];
|
||||
|
||||
foreach ($map as $key => [$col, $type, $maxLen]) {
|
||||
if (! array_key_exists($key, $data)) {
|
||||
continue;
|
||||
}
|
||||
$val = $data[$key];
|
||||
if ($type === 'string') {
|
||||
$val = mb_substr(strip_tags((string) $val), 0, $maxLen);
|
||||
} elseif ($type === 'integer') {
|
||||
$val = (int) $val;
|
||||
} elseif ($type === 'boolean') {
|
||||
$val = (bool) $val;
|
||||
}
|
||||
$update[$col] = $val;
|
||||
}
|
||||
|
||||
if ($update === []) {
|
||||
return response()->json(['error' => 'No fields to update'], 400);
|
||||
}
|
||||
|
||||
DB::table('items_base')->where('id', $id)->update($update);
|
||||
|
||||
Log::info('[Audit] FurniEditor update', [
|
||||
'user_id' => Auth::id(),
|
||||
'item_id' => $id,
|
||||
'fields' => array_keys($update),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$data = $request->json()->all();
|
||||
|
||||
$id = DB::table('items_base')->insertGetId([
|
||||
'sprite_id' => $data['spriteId'] ?? 0,
|
||||
'item_name' => $data['itemName'] ?? '',
|
||||
'public_name' => $data['publicName'] ?? '',
|
||||
'type' => $data['type'] ?? 's',
|
||||
'width' => $data['width'] ?? 1,
|
||||
'length' => $data['length'] ?? 1,
|
||||
'stack_height' => $data['stackHeight'] ?? 0,
|
||||
'allow_stack' => $data['allowStack'] ?? false,
|
||||
'allow_walk' => $data['allowWalk'] ?? false,
|
||||
'allow_sit' => $data['allowSit'] ?? false,
|
||||
'allow_lay' => $data['allowLay'] ?? false,
|
||||
'allow_gift' => $data['allowGift'] ?? true,
|
||||
'allow_trade' => $data['allowTrade'] ?? true,
|
||||
'allow_recycle' => $data['allowRecycle'] ?? false,
|
||||
'allow_marketplace_sell' => $data['allowMarketplaceSell'] ?? false,
|
||||
'allow_inventory_stack' => $data['allowInventoryStack'] ?? true,
|
||||
'interaction_type' => $data['interactionType'] ?? 'default',
|
||||
'interaction_modes_count' => $data['interactionModesCount'] ?? 1,
|
||||
]);
|
||||
|
||||
Log::info('[Audit] FurniEditor create', [
|
||||
'user_id' => Auth::id(),
|
||||
'new_id' => $id,
|
||||
]);
|
||||
|
||||
return response()->json(['id' => $id]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$id = (int) $request->input('id');
|
||||
if ($id === 0) {
|
||||
return response()->json(['error' => 'Missing id'], 400);
|
||||
}
|
||||
|
||||
DB::table('items_base')->where('id', $id)->delete();
|
||||
|
||||
Log::warning('[Audit] FurniEditor delete', [
|
||||
'user_id' => Auth::id(),
|
||||
'deleted_id' => $id,
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function interactions(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$rows = DB::table('items_base')
|
||||
->select('interaction_type')
|
||||
->groupBy('interaction_type')
|
||||
->orderBy('interaction_type')
|
||||
->pluck('interaction_type');
|
||||
|
||||
return response()->json(['interactions' => $rows]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function bySprite(Request $request)
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$spriteId = (int) $request->input('spriteId');
|
||||
if ($spriteId === 0) {
|
||||
return response()->json(['error' => 'Missing spriteId'], 400);
|
||||
}
|
||||
|
||||
$item = DB::table('items_base')->where('sprite_id', $spriteId)->first();
|
||||
if (! $item) {
|
||||
return response()->json(['error' => 'Item not found'], 404);
|
||||
}
|
||||
|
||||
return response()->json(['id' => $item->id]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Articles\WebsiteArticle;
|
||||
use App\Models\Game\Furniture\CatalogItem;
|
||||
use App\Models\Game\Furniture\CatalogPage;
|
||||
use App\Models\Game\Guild\Guild;
|
||||
use App\Models\Help\WebsiteHelpCenterTicket;
|
||||
use App\Models\Miscellaneous\CameraWeb;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\Shop\WebsiteShopArticle;
|
||||
use App\Models\User;
|
||||
use App\Services\Community\StaffService;
|
||||
use App\Services\User\UserApiService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class HotelApiController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserApiService $userApiService,
|
||||
private readonly StaffService $staffService,
|
||||
) {}
|
||||
|
||||
public function fetchUser(string $username): JsonResponse
|
||||
{
|
||||
$user = $this->userApiService->fetchUser($username, ['username', 'motto', 'look']);
|
||||
|
||||
return response()->json([
|
||||
'data' => $user,
|
||||
]);
|
||||
}
|
||||
|
||||
public function onlineUsers(): JsonResponse
|
||||
{
|
||||
$users = $this->userApiService->onlineUsers(['username', 'motto', 'look'], true);
|
||||
|
||||
return response()->json([
|
||||
'data' => $users,
|
||||
]);
|
||||
}
|
||||
|
||||
public function onlineUserCount(): JsonResponse
|
||||
{
|
||||
$count = $this->userApiService->onlineUserCount();
|
||||
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'onlineCount' => $count,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function articles(): JsonResponse
|
||||
{
|
||||
$articles = WebsiteArticle::with(['user:id,username,look'])
|
||||
->latest('id')
|
||||
->paginate(12);
|
||||
|
||||
return response()->json([
|
||||
'data' => $articles->items(),
|
||||
'meta' => [
|
||||
'current_page' => $articles->currentPage(),
|
||||
'last_page' => $articles->lastPage(),
|
||||
'per_page' => $articles->perPage(),
|
||||
'total' => $articles->total(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function article(string $slug): JsonResponse
|
||||
{
|
||||
$article = WebsiteArticle::with(['user:id,username,look', 'comments.user:id,username,look'])
|
||||
->where('slug', $slug)
|
||||
->firstOrFail();
|
||||
|
||||
return response()->json([
|
||||
'data' => $article,
|
||||
]);
|
||||
}
|
||||
|
||||
public function photos(): JsonResponse
|
||||
{
|
||||
$photos = CameraWeb::query()
|
||||
->where('visible', true)
|
||||
->latest('id')
|
||||
->paginate(12);
|
||||
|
||||
return response()->json([
|
||||
'data' => $photos->items(),
|
||||
'meta' => [
|
||||
'current_page' => $photos->currentPage(),
|
||||
'last_page' => $photos->lastPage(),
|
||||
'per_page' => $photos->perPage(),
|
||||
'total' => $photos->total(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function staff(): JsonResponse
|
||||
{
|
||||
$employees = $this->staffService->fetchStaffPositions();
|
||||
|
||||
return response()->json([
|
||||
'data' => $employees,
|
||||
]);
|
||||
}
|
||||
|
||||
public function shopPackages(Request $request): JsonResponse
|
||||
{
|
||||
$packages = WebsiteShopArticle::latest('id')->paginate(12);
|
||||
|
||||
$mapped = $packages->items()->map(fn ($pkg) => [
|
||||
'id' => $pkg->id,
|
||||
'title' => $pkg->name,
|
||||
'description' => $pkg->description,
|
||||
'price' => $pkg->price(),
|
||||
'credits' => null,
|
||||
'pixels' => null,
|
||||
'diamonds' => null,
|
||||
'image' => null,
|
||||
'currency' => 'credits',
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'data' => $mapped,
|
||||
'meta' => [
|
||||
'current_page' => $packages->currentPage(),
|
||||
'last_page' => $packages->lastPage(),
|
||||
'per_page' => $packages->perPage(),
|
||||
'total' => $packages->total(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function shopCategories(): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'data' => ['furniture', 'badges', 'ranks'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function teams(): JsonResponse
|
||||
{
|
||||
$teams = Guild::with('members')
|
||||
->latest('id')
|
||||
->paginate(12);
|
||||
|
||||
return response()->json([
|
||||
'data' => $teams->items(),
|
||||
'meta' => [
|
||||
'current_page' => $teams->currentPage(),
|
||||
'last_page' => $teams->lastPage(),
|
||||
'per_page' => $teams->perPage(),
|
||||
'total' => $teams->total(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function leaderboard(Request $request): JsonResponse
|
||||
{
|
||||
$type = $request->query('type', 'credits');
|
||||
$limit = (int) $request->query('limit', 100);
|
||||
|
||||
$validTypes = ['credits', 'pixels'];
|
||||
if (! in_array($type, $validTypes)) {
|
||||
$type = 'credits';
|
||||
}
|
||||
|
||||
$users = User::orderByDesc($type)
|
||||
->limit($limit)
|
||||
->get(['id', 'username', 'look', 'motto', 'credits', 'pixels']);
|
||||
|
||||
return response()->json([
|
||||
'data' => $users,
|
||||
'type' => $type,
|
||||
]);
|
||||
}
|
||||
|
||||
public function rareValues(Request $request): JsonResponse
|
||||
{
|
||||
$categoryId = $request->query('category');
|
||||
|
||||
$query = CatalogItem::query();
|
||||
|
||||
if ($categoryId) {
|
||||
$query->where('page_id', $categoryId);
|
||||
}
|
||||
|
||||
$items = $query->with('itemBase')
|
||||
->latest('id')
|
||||
->paginate(24);
|
||||
|
||||
return response()->json([
|
||||
'data' => $items->items(),
|
||||
'meta' => [
|
||||
'current_page' => $items->currentPage(),
|
||||
'last_page' => $items->lastPage(),
|
||||
'per_page' => $items->perPage(),
|
||||
'total' => $items->total(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function rareValuesCategories(): JsonResponse
|
||||
{
|
||||
$categories = CatalogPage::where('catalog_name', '!=', '')
|
||||
->where('visible', 1)
|
||||
->orderBy('order_number')
|
||||
->get(['id', 'catalog_name', 'icon']);
|
||||
|
||||
return response()->json([
|
||||
'data' => $categories,
|
||||
]);
|
||||
}
|
||||
|
||||
public function settings(): JsonResponse
|
||||
{
|
||||
$settings = Cache::remember('api_all_settings', 60, fn () => WebsiteSetting::all()->pluck('value', 'key'));
|
||||
|
||||
return response()->json([
|
||||
'data' => $settings,
|
||||
]);
|
||||
}
|
||||
|
||||
public function userProfile(string $username): JsonResponse
|
||||
{
|
||||
$user = User::where('username', $username)
|
||||
->firstOrFail();
|
||||
|
||||
return response()->json([
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'look' => $user->look,
|
||||
'motto' => $user->motto,
|
||||
'account_created' => $user->account_created,
|
||||
'online' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function helpTickets(Request $request): JsonResponse
|
||||
{
|
||||
$tickets = WebsiteHelpCenterTicket::with('user:id,username,look')
|
||||
->when($request->user(), fn ($q) => $q->where('user_id', $request->user()->id))
|
||||
->latest()
|
||||
->paginate(10);
|
||||
|
||||
return response()->json([
|
||||
'data' => $tickets->items(),
|
||||
'meta' => [
|
||||
'current_page' => $tickets->currentPage(),
|
||||
'last_page' => $tickets->lastPage(),
|
||||
'total' => $tickets->total(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function helpTicket(string $id): JsonResponse
|
||||
{
|
||||
$ticket = WebsiteHelpCenterTicket::with(['user:id,username,look', 'replies.user:id,username,look'])
|
||||
->where('id', $id)
|
||||
->firstOrFail();
|
||||
|
||||
return response()->json(['data' => $ticket]);
|
||||
}
|
||||
|
||||
public function helpTicketCreate(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'subject' => ['required', 'string', 'max:200'],
|
||||
'category' => ['required', 'string', 'max:100'],
|
||||
'message' => ['required', 'string', 'max:5000'],
|
||||
]);
|
||||
|
||||
$ticket = WebsiteHelpCenterTicket::create([
|
||||
'user_id' => $request->user()->id,
|
||||
'subject' => $validated['subject'],
|
||||
'category' => $validated['category'],
|
||||
'status' => 'open',
|
||||
]);
|
||||
|
||||
$ticket->replies()->create([
|
||||
'user_id' => $request->user()->id,
|
||||
'message' => $validated['message'],
|
||||
]);
|
||||
|
||||
return response()->json(['data' => $ticket], 201);
|
||||
}
|
||||
|
||||
public function helpTicketReply(Request $request, string $id): JsonResponse
|
||||
{
|
||||
$validated = $request->validate(['message' => 'required', 'string', 'max:5000']);
|
||||
|
||||
$ticket = WebsiteHelpCenterTicket::where('id', $id)
|
||||
->where('user_id', $request->user()->id)
|
||||
->firstOrFail();
|
||||
|
||||
$reply = $ticket->replies()->create([
|
||||
'user_id' => $request->user()->id,
|
||||
'message' => $validated['message'],
|
||||
]);
|
||||
|
||||
return response()->json(['data' => $reply->load('user:id,username,look')], 201);
|
||||
}
|
||||
|
||||
public function uploadPhoto(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'image' => ['required', 'image', 'max:5120'],
|
||||
]);
|
||||
|
||||
$path = $validated['image']->store('photos', 'public');
|
||||
|
||||
$photo = CameraWeb::create([
|
||||
'user_id' => $request->user()->id,
|
||||
'image' => '/storage/' . $path,
|
||||
'visible' => true,
|
||||
]);
|
||||
|
||||
return response()->json(['data' => $photo], 201);
|
||||
}
|
||||
|
||||
public function purchasePackage(Request $request, int $packageId): JsonResponse
|
||||
{
|
||||
$package = WebsiteShopArticle::findOrFail($packageId);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$cost = $package->costs;
|
||||
|
||||
if ($user->credits < $cost) {
|
||||
return response()->json(['error' => 'Not enough credits'], 400);
|
||||
}
|
||||
|
||||
$user->decrement('credits', $cost);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Purchase successful']);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Articles;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Articles\WebsiteArticle;
|
||||
use App\Services\Articles\ArticleService;
|
||||
use App\Services\Articles\ReactionService;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ArticleController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ArticleService $articlesService,
|
||||
private readonly ReactionService $reactionService,
|
||||
) {}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
$articles = $this->articlesService->getArticles(true);
|
||||
|
||||
return view('community.articles', [
|
||||
'articles' => $articles,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(WebsiteArticle $article): View
|
||||
{
|
||||
$reactions = $article->reactions()
|
||||
->with('user:id,username')
|
||||
->get();
|
||||
|
||||
return view('community.article', [
|
||||
'article' => $article,
|
||||
'otherArticles' => WebsiteArticle::whereKeyNot($article->id)->with('user:id,username,look')->latest('id')->take(15)->get(),
|
||||
'myReactions' => Auth::check() ? $reactions->where('user_id', Auth::id())->pluck('reaction') : [],
|
||||
'articleReactions' => $reactions->groupBy('reaction', true),
|
||||
]);
|
||||
}
|
||||
|
||||
public function toggleReaction(WebsiteArticle $article, Request $request): JsonResponse
|
||||
{
|
||||
$response = $this->reactionService->toggleReaction($article, Auth::user(), $request);
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Articles;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ArticleCommentFormRequest;
|
||||
use App\Models\Articles\WebsiteArticle;
|
||||
use App\Models\Articles\WebsiteArticleComment;
|
||||
use App\Services\Articles\CommentService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class WebsiteArticleCommentsController extends Controller
|
||||
{
|
||||
public function __construct(public readonly CommentService $commentService) {}
|
||||
|
||||
public function store(WebsiteArticle $article, ArticleCommentFormRequest $request): RedirectResponse
|
||||
{
|
||||
$this->commentService->store($request->get('comment'), $article);
|
||||
|
||||
return redirect()->back()->with('success', __('You comment has been posted!'));
|
||||
}
|
||||
|
||||
public function destroy(WebsiteArticleComment $comment): RedirectResponse
|
||||
{
|
||||
$this->commentService->destroy($comment);
|
||||
|
||||
return redirect()->back()->with('success', __('You comment has been deleted!'));
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\SocialAccount;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
class SocialAuthController extends Controller
|
||||
{
|
||||
public function redirect(string $provider)
|
||||
{
|
||||
$enabled = $this->isProviderEnabled($provider);
|
||||
|
||||
if (! $enabled) {
|
||||
return redirect()->route('login')
|
||||
->with('error', ucfirst($provider) . ' login is not enabled.');
|
||||
}
|
||||
|
||||
return Socialite::driver($provider)->redirect();
|
||||
}
|
||||
|
||||
public function callback(string $provider)
|
||||
{
|
||||
$enabled = $this->isProviderEnabled($provider);
|
||||
|
||||
if (! $enabled) {
|
||||
return redirect()->route('login')
|
||||
->with('error', ucfirst($provider) . ' login is not enabled.');
|
||||
}
|
||||
|
||||
try {
|
||||
$socialUser = Socialite::driver($provider)->user();
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('login')
|
||||
->with('error', 'Failed to login with ' . ucfirst($provider) . '. Please try again.');
|
||||
}
|
||||
|
||||
$account = SocialAccount::findByProvider($provider, $socialUser->getId());
|
||||
|
||||
if ($account) {
|
||||
Auth::login($account->user);
|
||||
|
||||
return $this->redirectAfterLogin();
|
||||
}
|
||||
|
||||
$email = $socialUser->getEmail();
|
||||
|
||||
if (! $email) {
|
||||
return redirect()->route('login')
|
||||
->with('error', 'Your ' . ucfirst($provider) . ' account does not have an email address linked.');
|
||||
}
|
||||
|
||||
$existingUser = User::where('mail', $email)->first();
|
||||
|
||||
if ($existingUser && $existingUser->hasSocialAccount($provider)) {
|
||||
return redirect()->route('login')
|
||||
->with('error', 'This ' . ucfirst($provider) . ' account is already linked to another user.');
|
||||
}
|
||||
|
||||
if ($existingUser && ! $existingUser->hasSocialAccount($provider)) {
|
||||
$existingUser->socialAccounts()->create([
|
||||
'provider' => $provider,
|
||||
'provider_id' => $socialUser->getId(),
|
||||
'avatar' => $socialUser->getAvatar(),
|
||||
]);
|
||||
|
||||
Auth::login($existingUser);
|
||||
|
||||
return $this->redirectAfterLogin();
|
||||
}
|
||||
|
||||
$username = $this->generateUsername($socialUser->getName() ?? $provider . '_user');
|
||||
|
||||
$user = User::create([
|
||||
'username' => $username,
|
||||
'mail' => $email,
|
||||
'password' => Hash::make(Str::random(16)),
|
||||
'account_created' => time(),
|
||||
'last_login' => time(),
|
||||
'motto' => 'New player',
|
||||
'look' => 'hr-100-61.hd-180-1.ch-210-66',
|
||||
'ip_register' => request()->ip(),
|
||||
'ip_current' => request()->ip(),
|
||||
]);
|
||||
|
||||
$user->socialAccounts()->create([
|
||||
'provider' => $provider,
|
||||
'provider_id' => $socialUser->getId(),
|
||||
'avatar' => $socialUser->getAvatar(),
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return $this->redirectAfterLogin();
|
||||
}
|
||||
|
||||
protected function isProviderEnabled(string $provider): bool
|
||||
{
|
||||
$key = 'social_login_' . $provider . '_enabled';
|
||||
|
||||
return setting($key) === '1';
|
||||
}
|
||||
|
||||
protected function generateUsername(string $name): string
|
||||
{
|
||||
$baseUsername = Str::slug($name, '');
|
||||
$username = $baseUsername;
|
||||
$counter = 1;
|
||||
|
||||
while (User::where('username', $username)->exists()) {
|
||||
$username = $baseUsername . $counter;
|
||||
$counter++;
|
||||
}
|
||||
|
||||
return $username;
|
||||
}
|
||||
|
||||
protected function redirectAfterLogin()
|
||||
{
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
public function unlink(Request $request, string $provider)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$user->socialAccounts()->where('provider', $provider)->delete();
|
||||
|
||||
return back()->with('success', ucfirst($provider) . ' account has been unlinked.');
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Badge;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class BadgeController extends Controller
|
||||
{
|
||||
public function show()
|
||||
{
|
||||
$cost = 150;
|
||||
$currencyType = 'credits';
|
||||
$folderError = false;
|
||||
$errorMessage = '';
|
||||
|
||||
$badgesPath = setting('badge_path_filesystem', storage_path('badges'));
|
||||
|
||||
if ($badgesPath) {
|
||||
$baseDir = base_path();
|
||||
$badgeDir = dirname((string) $badgesPath);
|
||||
|
||||
$realBaseDir = $baseDir;
|
||||
$realBadgeDir = $badgeDir;
|
||||
|
||||
try {
|
||||
$realBaseDir = @realpath($baseDir) ?: $baseDir;
|
||||
$realBadgeDir = @realpath($badgeDir) ?: $badgeDir;
|
||||
} catch (\Exception) {
|
||||
// Ignore realpath errors
|
||||
}
|
||||
|
||||
if ($realBadgeDir && ! str_starts_with($realBadgeDir, $realBaseDir)) {
|
||||
$badgesPath = storage_path('badges');
|
||||
}
|
||||
} else {
|
||||
$badgesPath = storage_path('badges');
|
||||
}
|
||||
|
||||
if (! file_exists($badgesPath)) {
|
||||
@mkdir($badgesPath, 0755, true);
|
||||
}
|
||||
|
||||
if (! is_writable($badgesPath)) {
|
||||
$folderError = true;
|
||||
$errorMessage = 'Badges folder is not writable.';
|
||||
}
|
||||
|
||||
return view('draw-badge', ['cost' => $cost, 'currencyType' => $currencyType, 'folderError' => $folderError, 'errorMessage' => $errorMessage]);
|
||||
}
|
||||
|
||||
public function buy(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (! $user) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in to purchase badges.');
|
||||
}
|
||||
|
||||
$cost = 150;
|
||||
|
||||
if (property_exists($user, 'credits') && $user->credits !== null && $user->credits < $cost) {
|
||||
return redirect()->back()->with('error', 'You don\'t have enough credits to purchase a badge.');
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', 'Badge purchase feature coming soon!');
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class FlashController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
return $this->renderClient('flash');
|
||||
}
|
||||
|
||||
private function renderClient(string $view): View
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
$user->update([
|
||||
'ip_current' => request()->ip(),
|
||||
]);
|
||||
|
||||
return view('client.' . $view, [
|
||||
'sso' => $user->ssoTicket(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class NitroController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
return $this->renderClient('nitro');
|
||||
}
|
||||
|
||||
private function renderClient(string $view): View
|
||||
{
|
||||
$user = auth()->user();
|
||||
$currentIp = request()->ip();
|
||||
|
||||
if ($user->ip_current !== $currentIp) {
|
||||
$user->update(['ip_current' => $currentIp]);
|
||||
}
|
||||
|
||||
$sso = $user->ssoTicket();
|
||||
|
||||
return view('client.' . $view, [
|
||||
'sso' => $sso,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, ValidatesRequests;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Help;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Help\WebsiteHelpCenterCategory;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class HelpCenterController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
$categories = Cache::remember('help_center_categories', 300, fn () => WebsiteHelpCenterCategory::orderBy('position')->get());
|
||||
|
||||
return view('help-center.index', [
|
||||
'categories' => $categories,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Help;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\WebsiteTicketFormRequest;
|
||||
use App\Models\Help\WebsiteHelpCenterCategory;
|
||||
use App\Models\Help\WebsiteHelpCenterTicket;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (! hasPermission('manage_website_tickets')) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot access this page'),
|
||||
]);
|
||||
}
|
||||
|
||||
return view('help-center.tickets.index', [
|
||||
'tickets' => WebsiteHelpCenterTicket::orderBy('open')->with('user:id,username')->paginate(15),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('help-center.tickets.create', [
|
||||
'categories' => WebsiteHelpCenterCategory::get(),
|
||||
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('user_id', Auth::id())->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(WebsiteTicketFormRequest $request)
|
||||
{
|
||||
Auth::user()->tickets()->create($request->validated());
|
||||
|
||||
return redirect()->back()->with('success', __('Ticket submitted!'));
|
||||
}
|
||||
|
||||
public function edit(WebsiteHelpCenterTicket $ticket)
|
||||
{
|
||||
if (! $ticket->canManageTicket()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot manage others tickets.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$ticket->load([
|
||||
'user:id,username,look',
|
||||
'category',
|
||||
'replies.user:id,username,look',
|
||||
]);
|
||||
|
||||
return view('help-center.tickets.edit', [
|
||||
'ticket' => $ticket,
|
||||
'categories' => WebsiteHelpCenterCategory::get(),
|
||||
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('id', '!=', $ticket->id)->where('user_id', Auth::id())->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(WebsiteHelpCenterTicket $ticket, WebsiteTicketFormRequest $request)
|
||||
{
|
||||
if (! $ticket->canManageTicket()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot manage others tickets.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$ticket->update($request->validated());
|
||||
|
||||
return to_route('help-center.ticket.show', $ticket)->with('success', __('Ticket updated!'));
|
||||
}
|
||||
|
||||
public function show(WebsiteHelpCenterTicket $ticket)
|
||||
{
|
||||
if (! $ticket->canManageTicket()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot view others tickets.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$ticket->load([
|
||||
'user:id,username,look',
|
||||
'category',
|
||||
'replies.user:id,username,look',
|
||||
]);
|
||||
|
||||
return view('help-center.tickets.show', [
|
||||
'ticket' => $ticket,
|
||||
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('id', '!=', $ticket->id)->where('user_id', Auth::id())->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(WebsiteHelpCenterTicket $ticket)
|
||||
{
|
||||
if (! $ticket->canDeleteTicket()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot delete others tickets.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$ticket->delete();
|
||||
|
||||
return to_route('me.show')->with('success', __('The ticket has been deleted!'));
|
||||
}
|
||||
|
||||
public function destroyReply(WebsiteHelpCenterTicketReply $reply)
|
||||
{
|
||||
if (! $reply->canDeleteReply()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You do not have permission to delete this reply.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$reply->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('The reply has been deleted!'));
|
||||
}
|
||||
|
||||
public function toggleTicketStatus(WebsiteHelpCenterTicket $ticket)
|
||||
{
|
||||
if (! $ticket->canManageTicket()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You manage others tickets.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$ticket->open ? $ticket->update(['open' => false]) : $ticket->update(['open' => true]);
|
||||
|
||||
return redirect()->back()->with('success', __('The ticket status has been changed!'));
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Help;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\WebsiteTicketReplyFormRequest;
|
||||
use App\Models\Help\WebsiteHelpCenterTicket;
|
||||
|
||||
class TicketReplyController extends Controller
|
||||
{
|
||||
public function store(WebsiteHelpCenterTicket $ticket, WebsiteTicketReplyFormRequest $request)
|
||||
{
|
||||
if (! $ticket->isOpen()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot reply to the ticket as it has been closed.'),
|
||||
]);
|
||||
}
|
||||
|
||||
if (! $ticket->canManageTicket()) {
|
||||
return redirect()->back()->with([
|
||||
'message' => __('You cannot reply to others tickets.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$data = $request->validated();
|
||||
$ticket->replies()->create([
|
||||
'user_id' => $request->user()->id,
|
||||
'content' => $data['content'],
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', __('The reply has been submitted!'));
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Help;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Help\WebsiteRuleCategory;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class WebsiteRulesController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
$categories = Cache::remember('website_rules_categories', 300, fn () => WebsiteRuleCategory::with('rules')->get());
|
||||
|
||||
return view('rules', [
|
||||
'categories' => $categories,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Miscellaneous;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Articles\WebsiteArticle;
|
||||
use App\Models\Miscellaneous\CameraWeb;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
$articles = Cache::remember('home_articles', 300, fn () => WebsiteArticle::with(['user:id,username,look'])
|
||||
->latest('id')
|
||||
->take(4)
|
||||
->get());
|
||||
|
||||
$photos = Cache::remember('home_photos', 300, fn () => CameraWeb::query()
|
||||
->where('visible', true)
|
||||
->latest('id')
|
||||
->take(4)
|
||||
->with('user:id,username,look')
|
||||
->get());
|
||||
|
||||
return view('index', [
|
||||
'articles' => $articles,
|
||||
'photos' => $photos,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Miscellaneous;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteInstallation;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Rules\ValidateInstallationKeyRule;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class InstallationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('installation.index');
|
||||
}
|
||||
|
||||
public function storeInstallationKey(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'installation_key' => ['required', 'string', 'max:255', new ValidateInstallationKeyRule],
|
||||
]);
|
||||
|
||||
WebsiteInstallation::first()->update([
|
||||
'step' => 1,
|
||||
'user_ip' => $request->ip(),
|
||||
]);
|
||||
|
||||
return to_route('installation.show-step', 1);
|
||||
}
|
||||
|
||||
public function showStep($currentStep)
|
||||
{
|
||||
$settings = $this->getSettingsForStep((int) $currentStep);
|
||||
|
||||
return view('installation.step-' . $currentStep, [
|
||||
'settings' => $settings,
|
||||
]);
|
||||
}
|
||||
|
||||
public function saveStepSettings(Request $request)
|
||||
{
|
||||
$this->updateSettings($request);
|
||||
|
||||
WebsiteInstallation::first()->increment('step');
|
||||
|
||||
return to_route('installation.show-step', WebsiteInstallation::first()->step);
|
||||
}
|
||||
|
||||
public function previousStep()
|
||||
{
|
||||
WebsiteInstallation::first()->decrement('step');
|
||||
|
||||
return to_route('installation.show-step', WebsiteInstallation::first()->step);
|
||||
}
|
||||
|
||||
public function restartInstallation()
|
||||
{
|
||||
WebsiteInstallation::first()->update([
|
||||
'step' => 0,
|
||||
'installation_key' => Str::uuid(),
|
||||
'user_ip' => null,
|
||||
]);
|
||||
|
||||
WebsiteSetting::where('key', 'theme')->update([
|
||||
'value' => 'atom',
|
||||
]);
|
||||
|
||||
return to_route('installation.index');
|
||||
}
|
||||
|
||||
public function completeInstallation()
|
||||
{
|
||||
// Clear all caches before marking as complete
|
||||
Cache::forget('website_permissions');
|
||||
Cache::forget('website_settings');
|
||||
|
||||
// Mark installation as complete
|
||||
WebsiteInstallation::latest()->first()->update([
|
||||
'completed' => true,
|
||||
]);
|
||||
|
||||
return to_route('welcome');
|
||||
}
|
||||
|
||||
private function updateSettings(Request $request): void
|
||||
{
|
||||
foreach ($request->except('_token') as $key => $value) {
|
||||
WebsiteSetting::where('key', '=', $key)->update([
|
||||
'value' => $value ?? '',
|
||||
]);
|
||||
}
|
||||
|
||||
// Cache will be automatically cleared by WebsiteSetting model events
|
||||
}
|
||||
|
||||
private function getSettingsForStep(int $step)
|
||||
{
|
||||
$settingsData = array_chunk(WebsiteSetting::all()->pluck('key')->toArray(), (int) ceil(WebsiteSetting::count() / 4));
|
||||
|
||||
$settings = match ($step) {
|
||||
1 => $settingsData[0] ?? [],
|
||||
2 => $settingsData[1] ?? [],
|
||||
3 => $settingsData[2] ?? [],
|
||||
4 => $settingsData[3] ?? [],
|
||||
5 => [], // Completion step has no settings
|
||||
default => throw new Exception('Step does not exist'),
|
||||
};
|
||||
|
||||
return WebsiteSetting::query()
|
||||
->whereIn('key', $settings)
|
||||
->select(['key', 'value', 'comment'])
|
||||
->get();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Miscellaneous;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteLanguage;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class LocaleController extends Controller
|
||||
{
|
||||
public function __invoke(string $locale): RedirectResponse
|
||||
{
|
||||
if (! WebsiteLanguage::where('country_code', $locale)->exists()) {
|
||||
return redirect()->back()->withErrors(['message' => __('The language selected is not supported')]);
|
||||
}
|
||||
|
||||
App::setLocale($locale);
|
||||
Session::put('locale', $locale);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Miscellaneous;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LogoGeneratorController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (! hasPermission('generate_logo')) {
|
||||
return to_route('me.show')->with([
|
||||
'message' => __('You do not have permission to do this.'),
|
||||
]);
|
||||
}
|
||||
|
||||
return view('logo-generator');
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate(['logo' => 'required|image|mimes:jpeg,png,gif,webp|max:5120']);
|
||||
|
||||
$path = $request->file('logo')->store('generated-logos', 'public');
|
||||
|
||||
$setting = WebsiteSetting::where('key', 'cms_logo')->first();
|
||||
|
||||
$setting?->update([
|
||||
'value' => '/storage/' . $path,
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Logo updated!']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Miscellaneous;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteMaintenanceTask;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\View\View;
|
||||
use Throwable;
|
||||
|
||||
class MaintenanceController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
$tasks = new LengthAwarePaginator([], 0, 5);
|
||||
|
||||
try {
|
||||
if (Schema::hasTable('website_maintenance_tasks')) {
|
||||
$tasks = WebsiteMaintenanceTask::with('user:id,username,look')->simplePaginate(5);
|
||||
}
|
||||
} catch (Throwable) {
|
||||
$tasks = new LengthAwarePaginator([], 0, 5);
|
||||
}
|
||||
|
||||
return view('maintenance', [
|
||||
'tasks' => $tasks,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\RadioContest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RadioContestController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$contests = RadioContest::where('is_active', true)
|
||||
->orderBy('starts_at', 'desc')
|
||||
->get();
|
||||
|
||||
return view('community.radio.contests.index', ['contests' => $contests]);
|
||||
}
|
||||
|
||||
public function show(RadioContest $contest): View|RedirectResponse
|
||||
{
|
||||
if (! $contest->is_active) {
|
||||
return redirect()->route('radio.contests.index')->with('error', __('radio.not_active'));
|
||||
}
|
||||
|
||||
return view('community.radio.contests.show', ['contest' => $contest]);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\RadioGiveaway;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RadioGiveawayController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$giveaways = RadioGiveaway::where('is_active', true)
|
||||
->orderBy('starts_at', 'desc')
|
||||
->get();
|
||||
|
||||
return view('community.radio.giveaways.index', ['giveaways' => $giveaways]);
|
||||
}
|
||||
|
||||
public function show(RadioGiveaway $giveaway): View|RedirectResponse
|
||||
{
|
||||
if (! $giveaway->is_active) {
|
||||
return redirect()->route('radio.giveaways.index')->with('error', __('radio.not_active'));
|
||||
}
|
||||
|
||||
return view('community.radio.giveaways.show', ['giveaway' => $giveaway]);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\PointsService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class RadioListenerPointController extends Controller
|
||||
{
|
||||
public function __construct(private readonly PointsService $pointsService) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$stats = $this->pointsService->getStats();
|
||||
$leaderboard = $this->pointsService->getLeaderboard(100);
|
||||
$weeklyLeaderboard = $this->pointsService->getWeeklyLeaderboard(50);
|
||||
$monthlyLeaderboard = $this->pointsService->getMonthlyLeaderboard(50);
|
||||
|
||||
return response()->json([
|
||||
'stats' => $stats,
|
||||
'leaderboard' => $leaderboard,
|
||||
'weekly_leaderboard' => $weeklyLeaderboard,
|
||||
'monthly_leaderboard' => $monthlyLeaderboard,
|
||||
]);
|
||||
}
|
||||
|
||||
public function leaderboard(Request $request): JsonResponse
|
||||
{
|
||||
$type = $request->get('type', 'all');
|
||||
$limit = min($request->get('limit', 50), 100);
|
||||
|
||||
$data = match ($type) {
|
||||
'weekly' => $this->pointsService->getWeeklyLeaderboard($limit),
|
||||
'monthly' => $this->pointsService->getMonthlyLeaderboard($limit),
|
||||
default => $this->pointsService->getLeaderboard($limit),
|
||||
};
|
||||
|
||||
return response()->json([
|
||||
'type' => $type,
|
||||
'limit' => $limit,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function userPoints(Request $request): JsonResponse
|
||||
{
|
||||
if (! auth()->check()) {
|
||||
return response()->json(['error' => 'Authentication required'], 401);
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
$points = $this->pointsService->getUserPoints($user);
|
||||
$rank = $this->pointsService->getUserRank($user);
|
||||
$history = $this->pointsService->getUserHistory($user, 20);
|
||||
|
||||
return response()->json([
|
||||
'points' => $points,
|
||||
'rank' => $rank,
|
||||
'history' => $history,
|
||||
]);
|
||||
}
|
||||
|
||||
public function stats(): JsonResponse
|
||||
{
|
||||
$stats = $this->pointsService->getStats();
|
||||
|
||||
return response()->json($stats);
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\RadioSongRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RadioSongRequestController extends Controller
|
||||
{
|
||||
private function isRequestsEnabled(): bool
|
||||
{
|
||||
return Cache::remember('radio_requests_enabled', 60, fn () => (bool) WebsiteSetting::where('key', 'radio_requests_enabled')->first()?->value);
|
||||
}
|
||||
|
||||
public function index(): View|RedirectResponse
|
||||
{
|
||||
if (! $this->isRequestsEnabled()) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.requests_disabled'));
|
||||
}
|
||||
|
||||
return view('community.radio.requests');
|
||||
}
|
||||
|
||||
public function store(RadioSongRequestFormRequest $request): RedirectResponse
|
||||
{
|
||||
if (! $this->isRequestsEnabled()) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.requests_disabled'));
|
||||
}
|
||||
|
||||
$validated = $request->validated();
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
$recentRequest = RadioSongRequest::where('user_id', $user->id)
|
||||
->where('submitted_at', '>', now()->subMinutes(10))
|
||||
->exists();
|
||||
|
||||
if ($recentRequest) {
|
||||
return back()->with('error', __('radio.request_cooldown'));
|
||||
}
|
||||
|
||||
RadioSongRequest::create([
|
||||
'user_id' => $user->id,
|
||||
'artist' => $validated['artist'],
|
||||
'title' => $validated['title'],
|
||||
'message' => $validated['message'] ?? null,
|
||||
'ip_address' => $request->ip(),
|
||||
'is_approved' => false,
|
||||
'submitted_at' => now(),
|
||||
'votes' => 0,
|
||||
'is_played' => false,
|
||||
]);
|
||||
|
||||
return back()->with('success', __('radio.request_submitted'));
|
||||
}
|
||||
|
||||
public function vote(RadioSongRequest $songRequest): RedirectResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if ($songRequest->is_played) {
|
||||
return back()->with('error', __('radio.already_played'));
|
||||
}
|
||||
|
||||
if ($songRequest->hasUserVoted($user)) {
|
||||
return back()->with('error', __('radio.already_voted'));
|
||||
}
|
||||
|
||||
$songRequest->addVote($user);
|
||||
|
||||
return back()->with('success', __('radio.vote_submitted'));
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Shop;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccountTopupFormRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Srmklive\PayPal\Services\PayPal;
|
||||
use Srmklive\PayPal\Services\PayPal as PayPalClient;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PayPalController extends Controller
|
||||
{
|
||||
private const string STATUS_CANCELLED = 'CANCELLED';
|
||||
|
||||
private const string STATUS_COMPLETED = 'COMPLETED';
|
||||
|
||||
private ?PayPalClient $provider = null;
|
||||
|
||||
private function getProvider(): PayPalClient
|
||||
{
|
||||
if (! $this->provider instanceof PayPal) {
|
||||
$this->provider = new PayPalClient;
|
||||
$this->provider->setApiCredentials(config('habbo.paypal'));
|
||||
$this->provider->getAccessToken();
|
||||
}
|
||||
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
public function process(AccountTopupFormRequest $request): Response|RedirectResponse
|
||||
{
|
||||
$amount = $request->integer('amount');
|
||||
$orderData = [
|
||||
'intent' => 'CAPTURE',
|
||||
'application_context' => [
|
||||
'return_url' => route('paypal.successful-transaction'),
|
||||
'cancel_url' => route('paypal.cancelled-transaction'),
|
||||
'brand_name' => setting('hotel_name'),
|
||||
'landing_page' => 'BILLING',
|
||||
'shipping_preference' => 'NO_SHIPPING',
|
||||
'user_action' => 'CONTINUE',
|
||||
],
|
||||
'purchase_units' => [
|
||||
0 => [
|
||||
'amount' => [
|
||||
'currency_code' => config('habbo.paypal.currency'),
|
||||
'value' => (string) $amount,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$response = $this->getProvider()->createOrder($orderData);
|
||||
|
||||
if (isset($response['id']) === false) {
|
||||
Log::error('Error creating order', ['response' => $response]);
|
||||
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => $response['message'] ?? __('Something went wrong')],
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($response['links'] as $links) {
|
||||
if ($links['rel'] === 'approve') {
|
||||
$request->user()->transactions()->create([
|
||||
'transaction_id' => $response['id'],
|
||||
'amount' => 0,
|
||||
]);
|
||||
|
||||
return redirect()->away($links['href']);
|
||||
}
|
||||
}
|
||||
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => $response['message'] ?? __('Something went wrong')],
|
||||
);
|
||||
}
|
||||
|
||||
public function successful(Request $request): Response
|
||||
{
|
||||
$request->validate([
|
||||
'token' => 'required',
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$transaction = $user->transactions()->where('transaction_id', $request['token'])->first();
|
||||
if ($transaction === null) {
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please try again later')]);
|
||||
}
|
||||
|
||||
$response = $this->getProvider()->capturePaymentOrder($request['token']);
|
||||
$paymentDetails = $response['purchase_units'][0]['payments']['captures'][0];
|
||||
|
||||
if (! isset($response['status'], $paymentDetails)) {
|
||||
Log::error('Invalid response from PayPal', ['response' => $response]);
|
||||
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please try again later')]);
|
||||
}
|
||||
|
||||
if (($response['status'] ?? null) === null) {
|
||||
$details = $response['error']['details'][0];
|
||||
$transaction->update([
|
||||
'status' => $response['name'],
|
||||
'description' => sprintf('%s - %s', $details['issue'], $details['description']),
|
||||
'amount' => 0,
|
||||
]);
|
||||
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please check your paypal account to make sure nothing was deducted and try again')]);
|
||||
}
|
||||
|
||||
$paymentDetails = $response['purchase_units'][0]['payments']['captures'][0];
|
||||
|
||||
$transaction->update([
|
||||
'status' => $paymentDetails['status'],
|
||||
'amount' => $paymentDetails['amount']['value'],
|
||||
'currency' => $paymentDetails['amount']['currency_code'],
|
||||
]);
|
||||
|
||||
if ($response['status'] !== self::STATUS_COMPLETED) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => $response['message'] ?? __('Something went wrong')],
|
||||
);
|
||||
}
|
||||
|
||||
$user->increment('website_balance', $paymentDetails['amount']['value']);
|
||||
|
||||
return to_route('shop.index')->with('success', __('Transaction successful'));
|
||||
}
|
||||
|
||||
public function cancelled(Request $request): Response
|
||||
{
|
||||
$request->validate([
|
||||
'token' => 'required',
|
||||
]);
|
||||
|
||||
$transaction = $request->user()->transactions()->where('transaction_id', $request['token'])->first();
|
||||
if ($transaction !== null) {
|
||||
$transaction->update([
|
||||
'status' => self::STATUS_CANCELLED,
|
||||
'description' => 'The user cancelled the transaction',
|
||||
]);
|
||||
}
|
||||
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('You have canceled the transaction')],
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Shop;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Shop\WebsiteShopArticle;
|
||||
use App\Models\Shop\WebsiteShopCategory;
|
||||
use App\Models\User;
|
||||
use App\Services\PurchaseService;
|
||||
use App\Services\RconService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShopController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RconService $rconService,
|
||||
private readonly PurchaseService $purchaseService,
|
||||
) {}
|
||||
|
||||
public function __invoke(?WebsiteShopCategory $category)
|
||||
{
|
||||
$query = WebsiteShopArticle::query()->orderBy('position');
|
||||
|
||||
if ($category && $category->exists) {
|
||||
$query = $category->articles()->orderBy('position');
|
||||
}
|
||||
|
||||
return view('shop.shop', [
|
||||
'articles' => $query->with(['rank:id,rank_name', 'features'])->get(),
|
||||
'categories' => WebsiteShopCategory::whereHas('articles')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function purchase(WebsiteShopArticle $package, Request $request): Response
|
||||
{
|
||||
/** @var User $currentUser */
|
||||
$currentUser = Auth::user();
|
||||
|
||||
$recipient = null;
|
||||
|
||||
if ($request->has('receiver')) {
|
||||
if (! $package->is_giftable) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('This package is not giftable')],
|
||||
);
|
||||
}
|
||||
|
||||
$recipient = User::where('username', $request->input('receiver'))->first();
|
||||
|
||||
if (! $recipient) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('Recipient not found')],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$user = $recipient ?? $currentUser;
|
||||
|
||||
if ($package->give_rank && $user->rank >= $package->give_rank) {
|
||||
$message = __('You are already this or a higher rank');
|
||||
|
||||
if ($recipient && $user->username !== $currentUser->username) {
|
||||
$message = __('The recipient is already this or a higher rank');
|
||||
}
|
||||
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => $message],
|
||||
);
|
||||
}
|
||||
|
||||
if (! $this->rconService->isConnected && (int) $user->online === 1) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('Please logout before purchasing a package')],
|
||||
);
|
||||
}
|
||||
|
||||
if ($currentUser->website_balance < $package->price()) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('You need to top-up your account with another $:amount to purchase this package', ['amount' => ($package->price() - $currentUser->website_balance)])],
|
||||
);
|
||||
}
|
||||
|
||||
$message = $this->purchaseService->processPurchase($currentUser, $package, $recipient);
|
||||
|
||||
return to_route('shop.index')->with('success', $message);
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Shop;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ShopVoucherFormRequest;
|
||||
use App\Models\Shop\WebsiteShopVoucher;
|
||||
|
||||
class ShopVoucherController extends Controller
|
||||
{
|
||||
public function __invoke(ShopVoucherFormRequest $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
$voucher = WebsiteShopVoucher::where('code', $request->string('code'))->first();
|
||||
|
||||
if (is_null($voucher) || ($voucher->expires_at && $voucher->expires_at->lte(now()))) {
|
||||
return redirect()->back()->withErrors([
|
||||
'message' => __('No active voucher with the given code was found'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($user->usedShopVouchers()->where('voucher_id', $voucher->id)->exists()) {
|
||||
return redirect()->back()->withErrors([
|
||||
'message' => __('You can only use each shop voucher once'),
|
||||
]);
|
||||
}
|
||||
|
||||
$user->usedShopVouchers()->create([
|
||||
'voucher_id' => $voucher->id,
|
||||
]);
|
||||
|
||||
$user->increment('website_balance', $voucher->amount);
|
||||
|
||||
$voucher->increment('use_count');
|
||||
|
||||
if ($voucher->max_uses && $voucher->use_count >= $voucher->max_uses) {
|
||||
$voucher->update([
|
||||
'expires_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', __('Your balance has been increased by $:amount', ['amount' => $voucher->amount]));
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccountSettingsFormRequest;
|
||||
use App\Services\RconService;
|
||||
use App\Services\User\SessionService;
|
||||
use App\Services\User\UserService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AccountSettingsController extends Controller
|
||||
{
|
||||
public function __construct(private readonly SessionService $sessionService, private readonly UserService $userService, private readonly RconService $rconService) {}
|
||||
|
||||
public function edit(): View
|
||||
{
|
||||
return view('user.settings.account', [
|
||||
'user' => Auth::user()->load('settings:allow_name_change'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function sessionLogs(Request $request): View
|
||||
{
|
||||
$sessions = $this->sessionService->fetchSessionLogs($request);
|
||||
|
||||
return view('user.settings.session-logs', [
|
||||
'logs' => $sessions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(AccountSettingsFormRequest $request): RedirectResponse
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user === null) {
|
||||
return redirect()->back()->withErrors('User not found');
|
||||
}
|
||||
|
||||
if ($user->mail !== $request->input('mail')) {
|
||||
$this->userService->updateField($user, 'mail', $request->input('mail'));
|
||||
}
|
||||
|
||||
if ($user->motto !== $request->input('motto')) {
|
||||
$this->rconService->setMotto($user, $request->input('motto'));
|
||||
$this->userService->updateField($user, 'motto', $request->input('motto'));
|
||||
}
|
||||
|
||||
return redirect()->route('settings.account.show')->with('success', __('Your account settings has been updated'));
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User\Ban;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class BannedController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
$ipBan = Ban::where('ip', '=', request()->ip())
|
||||
->where('ban_expire', '>', time())
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
return view('banned', [
|
||||
'ban' => $ipBan ?? Auth::user()->ban,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PasswordResetToken;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
return view('auth.passwords.forget');
|
||||
}
|
||||
|
||||
public function submitForgetPassword(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'mail' => 'required|email',
|
||||
]);
|
||||
|
||||
// Do not tell the user that this email does not exist to prevent possible attacks
|
||||
if (User::where('mail', $request->mail)->exists()) {
|
||||
$token = Str::uuid();
|
||||
PasswordResetToken::create([
|
||||
'email' => $request->mail,
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
Mail::send('email.forgetPassword', ['token' => $token], function ($message) use ($request) {
|
||||
$message->to($request->mail);
|
||||
$message->subject('Reset Password');
|
||||
});
|
||||
}
|
||||
|
||||
return back()->with('success', __('We have e-mailed your password reset link!'));
|
||||
}
|
||||
|
||||
public function showResetPassword(Request $request, string $token)
|
||||
{
|
||||
$prt = PasswordResetToken::select(['token', 'created_at'])->where('token', $token)->first();
|
||||
if ($prt === null) {
|
||||
return to_route('forgot.password.get')->withErrors('message', __('This token has expired!'));
|
||||
}
|
||||
$tokenExpiration = Carbon::now()->subMinutes(config('habbo.password_reset_token_time'));
|
||||
if ($prt->created_at->lt($tokenExpiration)) {
|
||||
$prt->delete();
|
||||
|
||||
return to_route('forgot.password.get')->withErrors('message', __('This token has expired!'));
|
||||
}
|
||||
|
||||
return view('auth.passwords.reset', [
|
||||
'token' => $token,
|
||||
]);
|
||||
}
|
||||
|
||||
public function submitResetPassword(Request $request, string $token)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required|min:8|confirmed',
|
||||
'password_confirmation' => 'required',
|
||||
]);
|
||||
|
||||
$prt = PasswordResetToken::select(['email', 'token'])->where('token', $token)->first();
|
||||
if ($prt === null) {
|
||||
return to_route('forgot.password.get')->withErrors('message', __('This token has expired!'));
|
||||
}
|
||||
|
||||
$prt->user->changePassword($request->password);
|
||||
$prt->delete();
|
||||
|
||||
return to_route('login')->with('success', __('Your password has been successfully reset!'));
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\GuestbookFormRequest;
|
||||
use App\Models\User;
|
||||
use App\Models\User\WebsiteUserGuestbook;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class GuestbookController extends Controller
|
||||
{
|
||||
public function store(User $user, GuestbookFormRequest $request)
|
||||
{
|
||||
$this->validateGuestbookPost($user, $request);
|
||||
|
||||
$user->profileGuestbook()->create([
|
||||
'user_id' => Auth::id(),
|
||||
'message' => $request->input('message'),
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', __('Your message has been posted.'));
|
||||
}
|
||||
|
||||
public function destroy(User $user, WebsiteUserGuestbook $guestbook)
|
||||
{
|
||||
if ($guestbook->user_id !== Auth::id() && $guestbook->profile_id !== $user->id && Auth::user()->rank < (int) setting('min_staff_rank')) {
|
||||
return redirect()->back()->withErrors([
|
||||
'message' => __('Do do not have permission to delete this message'),
|
||||
]);
|
||||
}
|
||||
|
||||
$guestbook->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('Your message has been deleted.'));
|
||||
}
|
||||
|
||||
private function validateGuestbookPost(User $user, GuestbookFormRequest $request)
|
||||
{
|
||||
if ($user->id === $request->user()->id) {
|
||||
return $this->redirectWithError(__('You cannot post a message on your own profile.'));
|
||||
}
|
||||
|
||||
$maxAllowedPostCount = empty(setting('max_guestbook_posts_per_profile')) ? 3 : (int) setting('max_guestbook_posts_per_profile');
|
||||
if ($user->profileGuestbook()->where('user_id', $request->user()->id)->count() >= $maxAllowedPostCount) {
|
||||
return $this->redirectWithError(__('You have already posted :count messages on this profile.', ['count' => $maxAllowedPostCount]));
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectWithError($message)
|
||||
{
|
||||
return redirect()->back()->withErrors(['message' => $message]);
|
||||
}
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Articles\WebsiteArticle;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class MeController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
return view('user.me', [
|
||||
'onlineFriends' => $user->getOnlineFriends(),
|
||||
'user' => $user->load('permission:id,rank_name'),
|
||||
'articles' => WebsiteArticle::with('user:id,username,look')->latest()->take(5)->get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\PasswordSettingsFormRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PasswordSettingsController extends Controller
|
||||
{
|
||||
public function edit(): View
|
||||
{
|
||||
return view('user.settings.password');
|
||||
}
|
||||
|
||||
public function update(PasswordSettingsFormRequest $request): RedirectResponse
|
||||
{
|
||||
Auth::user()->update([
|
||||
'password' => Hash::make($request->input('password')),
|
||||
]);
|
||||
|
||||
return redirect()->route('settings.password.show')->with('success', __('Your password has been changed!'));
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PreferencesController extends Controller
|
||||
{
|
||||
public function edit(): View
|
||||
{
|
||||
$user = Auth::user();
|
||||
$preferences = $user->preferences ?? $this->getDefaultPreferences();
|
||||
|
||||
return view('user.settings.preferences', [
|
||||
'preferences' => $preferences,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request): RedirectResponse
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'radio_auto_play' => 'sometimes|boolean',
|
||||
'radio_shouts' => 'sometimes|boolean',
|
||||
'radio_notifications' => 'sometimes|boolean',
|
||||
'friend_requests' => 'sometimes|boolean',
|
||||
'room_invites' => 'sometimes|boolean',
|
||||
'email_notifications' => 'sometimes|boolean',
|
||||
]);
|
||||
|
||||
$preferences = array_merge($this->getDefaultPreferences(), $validated);
|
||||
$user->preferences = $preferences;
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('settings.preferences.show')->with('success', __('Preferences updated successfully'));
|
||||
}
|
||||
|
||||
private function getDefaultPreferences(): array
|
||||
{
|
||||
return [
|
||||
'radio_auto_play' => false,
|
||||
'radio_shouts' => true,
|
||||
'radio_notifications' => true,
|
||||
'friend_requests' => true,
|
||||
'room_invites' => true,
|
||||
'email_notifications' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function __invoke(User $user)
|
||||
{
|
||||
$user->load([
|
||||
'friends.friend:id,username,look',
|
||||
'guilds.guild:id,name,badge',
|
||||
'profileGuestbook.user:id,username,look',
|
||||
'photos',
|
||||
'badges',
|
||||
]);
|
||||
|
||||
$showStats = WebsiteSetting::where('key', 'profile_show_stats')->first()?->value ?? '1';
|
||||
$showOnline = WebsiteSetting::where('key', 'profile_show_online_status')->first()?->value ?? '1';
|
||||
|
||||
$accountAge = $this->getAccountAge($user->account_created);
|
||||
$lastLogin = $this->getLastLogin($user->last_login);
|
||||
$totalFriends = $user->friends()->count();
|
||||
$totalGuilds = $user->guilds()->count();
|
||||
|
||||
return view('user.profile', [
|
||||
'user' => $user,
|
||||
'friends' => $user->friends->take(10),
|
||||
'groups' => $user->guilds->take(5),
|
||||
'guestbook' => $user->profileGuestbook->take(5),
|
||||
'photos' => $user->photos->take(3),
|
||||
'badges' => $user->badges->take(3),
|
||||
'showStats' => $showStats,
|
||||
'showOnline' => $showOnline,
|
||||
'accountAge' => $accountAge,
|
||||
'lastLogin' => $lastLogin,
|
||||
'totalFriends' => $totalFriends,
|
||||
'totalGuilds' => $totalGuilds,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getAccountAge(int $timestamp): string
|
||||
{
|
||||
$created = Carbon::createFromTimestamp($timestamp);
|
||||
$now = Carbon::now();
|
||||
$days = $created->diffInDays($now);
|
||||
|
||||
if ($days < 7) {
|
||||
return $days . ' day' . ($days !== 1 ? 's' : '');
|
||||
} elseif ($days < 30) {
|
||||
$weeks = floor($days / 7);
|
||||
|
||||
return $weeks . ' week' . ($weeks !== 1 ? 's' : '');
|
||||
} elseif ($days < 365) {
|
||||
$months = floor($days / 30);
|
||||
|
||||
return $months . ' month' . ($months !== 1 ? 's' : '');
|
||||
} else {
|
||||
$years = floor($days / 365);
|
||||
|
||||
return $years . ' year' . ($years !== 1 ? 's' : '');
|
||||
}
|
||||
}
|
||||
|
||||
private function getLastLogin(int $timestamp): string
|
||||
{
|
||||
$lastLogin = Carbon::createFromTimestamp($timestamp);
|
||||
$now = Carbon::now();
|
||||
$diff = $now->diffInMinutes($lastLogin);
|
||||
|
||||
if ($diff < 1) {
|
||||
return 'Just now';
|
||||
} elseif ($diff < 60) {
|
||||
return $diff . ' minute' . ($diff !== 1 ? 's' : '') . ' ago';
|
||||
} elseif ($diff < 1440) {
|
||||
$hours = floor($diff / 60);
|
||||
|
||||
return $hours . ' hour' . ($hours !== 1 ? 's' : '') . ' ago';
|
||||
} elseif ($diff < 10080) {
|
||||
$days = floor($diff / 1440);
|
||||
|
||||
return $days . ' day' . ($days !== 1 ? 's' : '') . ' ago';
|
||||
} else {
|
||||
return $lastLogin->format('d M Y');
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\RconService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ReferralController extends Controller
|
||||
{
|
||||
public function __invoke(RconService $rcon): RedirectResponse
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (! $user->referrals || $user->referrals->referrals_total < setting('referrals_needed')) {
|
||||
return redirect()->back()->withErrors([
|
||||
'message' => __('You do not have enough referrals to claim your reward'),
|
||||
]);
|
||||
}
|
||||
|
||||
// Decrease the total amount of referrals with the amount needed to claim reward
|
||||
$user->referrals->decrement('referrals_total', setting('referrals_needed'));
|
||||
|
||||
$rcon->giveDiamonds($user, setting('referral_reward_amount'));
|
||||
|
||||
// Log the claim
|
||||
$user->claimedReferralLog()->create([
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', __('Woah! You have successfully claimed your reward - Keep up the good work!'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
use Laravel\Fortify\Actions\DisableTwoFactorAuthentication;
|
||||
use Laravel\Fortify\Actions\EnableTwoFactorAuthentication;
|
||||
|
||||
class TwoFactorAuthenticationController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
return view('user.settings.two-factor');
|
||||
}
|
||||
|
||||
public function store(Request $request, EnableTwoFactorAuthentication $enable): RedirectResponse
|
||||
{
|
||||
$enable($request->user());
|
||||
|
||||
return redirect()->route('settings.two-factor')->with('success', __('Two-factor authentication has been enabled. Please scan the QR code to continue.'));
|
||||
}
|
||||
|
||||
public function verify(Request $request): RedirectResponse
|
||||
{
|
||||
$confirmed = $request->user()->confirmTwoFactorAuthentication($request->input('code'));
|
||||
if (! $confirmed) {
|
||||
return back()->withErrors('Invalid Two Factor Authentication code');
|
||||
}
|
||||
|
||||
return redirect()->route('settings.two-factor')->with('success', __('Two-factor authentication has been confirmed.'));
|
||||
}
|
||||
|
||||
public function destroy(Request $request, DisableTwoFactorAuthentication $disable): RedirectResponse
|
||||
{
|
||||
$disable($request->user());
|
||||
|
||||
return redirect()->route('settings.two-factor')->with('success', __('Two-factor authentication has been disabled.'));
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
|
||||
class UserReferralController extends Controller
|
||||
{
|
||||
public function __invoke(string $referralCode)
|
||||
{
|
||||
if (! User::where('referral_code', $referralCode)->exists()) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return view('auth.register', [
|
||||
'referral_code' => $referralCode,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user