Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 additions & 18 deletions app/Http/Controllers/FileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,20 @@
use App\Models\Page;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Inertia\Inertia;

class FileController extends Controller
{

/**
* Display files for a mod.
*/
public function index(Mod $mod)
{
$user = Auth::user();

if (!$mod->canBeAccessedBy($user)) {
if (! $mod->canBeAccessedBy($user)) {
abort(403);
}

Expand Down Expand Up @@ -64,7 +62,7 @@ public function store(Request $request, Mod $mod)
{
$user = Auth::user();

if (!$mod->userCan($user, 'edit')) {
if (! $mod->userCan($user, 'edit')) {
abort(403);
}
$request->validate([
Expand All @@ -88,7 +86,7 @@ public function store(Request $request, Mod $mod)
foreach ($uploadedFiles as $uploadedFile) {
$originalName = $uploadedFile->getClientOriginalName();
$extension = $uploadedFile->getClientOriginalExtension();
$filename = Str::uuid() . '.' . $extension;
$filename = Str::uuid().'.'.$extension;

$path = "mods/{$mod->id}/files/{$filename}";

Expand All @@ -103,7 +101,7 @@ public function store(Request $request, Mod $mod)
'path' => $path,
'mime_type' => $uploadedFile->getMimeType(),
'size' => $uploadedFile->getSize(),
'storage_driver' => "local", // hard coded local for now
'storage_driver' => 'local', // hard coded local for now
'uploaded_by' => $user->id,
]);
$url = Storage::disk('public')->url($path);
Expand All @@ -122,11 +120,11 @@ public function store(Request $request, Mod $mod)
return response()->json([
'success' => true,
'files' => $uploadedFileData,
'message' => count($uploadedFiles) . ' file(s) uploaded successfully!'
'message' => count($uploadedFiles).' file(s) uploaded successfully!',
]);
}

return redirect()->back()->with('success', count($uploadedFiles) . ' file(s) uploaded successfully!');
return redirect()->back()->with('success', count($uploadedFiles).' file(s) uploaded successfully!');
}

/**
Expand All @@ -140,7 +138,7 @@ public function show(Mod $mod, File $file)
abort(404);
}

if (!$mod->canBeAccessedBy($user)) {
if (! $mod->canBeAccessedBy($user)) {
abort(403);
}

Expand All @@ -164,13 +162,13 @@ public function download(Mod $mod, File $file)
abort(404);
}

if (!$mod->canBeAccessedBy($user)) {
if (! $mod->canBeAccessedBy($user)) {
abort(403);
}

$disk = 'public';

if (!Storage::disk($disk)->exists($file->path)) {
if (! Storage::disk($disk)->exists($file->path)) {
abort(404, 'File not found on storage.');
}

Expand All @@ -188,7 +186,7 @@ public function destroy(Mod $mod, File $file)
abort(404);
}

if (!$mod->userCan($user, 'edit')) {
if (! $mod->userCan($user, 'edit')) {
abort(403);
}

Expand All @@ -208,7 +206,7 @@ public function quickUpload(Request $request, Mod $mod)
{
$user = Auth::user();

if (!$mod->userCan($user, 'edit')) {
if (! $mod->userCan($user, 'edit')) {
abort(403);
}

Expand All @@ -221,16 +219,16 @@ public function quickUpload(Request $request, Mod $mod)
$allowedMimes = [
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
'application/pdf', 'text/plain', 'text/markdown',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
];

if (!in_array($uploadedFile->getMimeType(), $allowedMimes)) {
if (! in_array($uploadedFile->getMimeType(), $allowedMimes)) {
return response()->json(['error' => 'File type not allowed.'], 422);
}

$originalName = $uploadedFile->getClientOriginalName();
$extension = $uploadedFile->getClientOriginalExtension();
$filename = Str::uuid() . '.' . $extension;
$filename = Str::uuid().'.'.$extension;

$path = "mods/{$mod->id}/files/{$filename}";
$disk = 'public';
Expand Down Expand Up @@ -260,7 +258,7 @@ public function quickUpload(Request $request, Mod $mod)
'size' => $file->human_size,
'is_image' => $file->isImage(),
],
'message' => 'File uploaded successfully!'
'message' => 'File uploaded successfully!',
]);
}

Expand All @@ -275,7 +273,7 @@ public function getPageFiles(Mod $mod, Page $page)
abort(404);
}

if (!$mod->canBeAccessedBy($user)) {
if (! $mod->canBeAccessedBy($user)) {
abort(403);
}

Expand Down
87 changes: 60 additions & 27 deletions app/Http/Controllers/ModController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@

class ModController extends Controller
{

/**
* Display a listing of user's mods.
*/
public function index()
{
$user = Auth::user();

if (!$user) {
if (! $user) {
return redirect()->route('login');
}

Expand Down Expand Up @@ -69,7 +68,7 @@ public function store(Request $request)
$counter = 1;

while (Mod::where('slug', $slug)->exists()) {
$slug = $originalSlug . '-' . $counter;
$slug = $originalSlug.'-'.$counter;
$counter++;
}

Expand All @@ -93,7 +92,7 @@ public function show(Mod $mod)
{
$user = Auth::user();

if ($mod->visibility === 'private' && !$mod->canBeAccessedBy($user)) {
if ($mod->visibility === 'private' && ! $mod->canBeAccessedBy($user)) {
abort(403);
}

Expand All @@ -103,7 +102,7 @@ public function show(Mod $mod)
'rootPages' => function ($query) {
$query->published()->with('publishedChildren');
},
'indexPage'
'indexPage',
]);

$userRole = $user ? $mod->getUserRole($user) : null;
Expand All @@ -123,7 +122,7 @@ public function edit(Mod $mod)
{
$user = Auth::user();

if (!$mod->userCan($user, 'manage_settings')) {
if (! $mod->userCan($user, 'manage_settings')) {
abort(403);
}

Expand All @@ -139,7 +138,7 @@ public function update(Request $request, Mod $mod)
{
$user = Auth::user();

if (!$mod->userCan($user, 'manage_settings')) {
if (! $mod->userCan($user, 'manage_settings')) {
abort(403);
}

Expand All @@ -156,7 +155,7 @@ public function update(Request $request, Mod $mod)
$counter = 1;

while (Mod::where('slug', $slug)->where('id', '!=', $mod->id)->exists()) {
$slug = $originalSlug . '-' . $counter;
$slug = $originalSlug.'-'.$counter;
$counter++;
}

Expand Down Expand Up @@ -193,7 +192,7 @@ public function manageCollaborators(Mod $mod)
{
$user = Auth::user();

if (!$mod->userCan($user, 'manage_collaborators')) {
if (! $mod->userCan($user, 'manage_collaborators')) {
abort(403);
}

Expand All @@ -212,23 +211,29 @@ public function addCollaborator(Request $request, Mod $mod)
{
$user = Auth::user();

if (!$mod->userCan($user, 'manage_collaborators')) {
if (! $mod->userCan($user, 'manage_collaborators')) {
abort(403);
}

$validated = $request->validate([
'username' => 'required|string|exists:users,username',
'username' => 'required|string',
'role' => 'required|in:admin,editor,viewer',
]);

$collaborator = User::where('username', $validated['username'])->firstOrFail();
$collaborator = User::where('username', $validated['username'])
->orWhere('email', $validated['username'])
->first();

if (! $collaborator) {
return back()->withErrors(['email' => 'User not found.']);
}

if ($mod->collaborators()->where('user_id', $collaborator->id)->exists()) {
return back()->withErrors(['username' => 'User is already a collaborator.']);
return back()->withErrors(['email' => 'User is already a collaborator.']);
}

if ($mod->owner_id === $collaborator->id) {
return back()->withErrors(['username' => 'Owner cannot be added as collaborator.']);
return back()->withErrors(['email' => 'Owner cannot be added as collaborator.']);
}

$existingInvitation = ModInvitation::where('mod_id', $mod->id)
Expand All @@ -238,11 +243,17 @@ public function addCollaborator(Request $request, Mod $mod)
->first();

if ($existingInvitation) {
return back()->withErrors(['username' => 'User already has a pending invitation.']);
return back()->withErrors(['email' => 'User already has a pending invitation.']);
}

$invitation = ModInvitation::createInvitation($mod, $collaborator, $user, $validated['role']);
// Add collaborator directly to mod_users table
$mod->collaborators()->attach($collaborator->id, [
'role' => $validated['role'],
'invited_by' => $user->id,
]);

// Also create and send invitation email
$invitation = ModInvitation::createInvitation($mod, $collaborator, $user, $validated['role']);
$inviteUrl = route('invitations.show', ['token' => $invitation->token]);

try {
Expand All @@ -256,8 +267,7 @@ public function addCollaborator(Request $request, Mod $mod)

return back()->with('success', "Invitation sent to {$collaborator->name}!");
} catch (\Exception $e) {
$invitation->delete();
return back()->withErrors(['email' => 'Failed to send invitation email. Please try again.']);
return back()->with('success', 'Collaborator added successfully! (Email notification failed to send)');
}
}

Expand All @@ -268,7 +278,22 @@ public function removeCollaborator(Mod $mod, User $collaborator)
{
$user = Auth::user();

if (!$mod->userCan($user, 'manage_collaborators')) {
// Allow users to remove themselves
if ($user->id === $collaborator->id) {
$mod->collaborators()->detach($collaborator->id);

return back()->with('success', 'You have left the mod successfully!');
}

if (! $mod->userCan($user, 'manage_collaborators')) {
abort(403);
}

$currentUserRole = $mod->getUserRole($user);
$targetUserRole = $mod->getUserRole($collaborator);

// Admins cannot remove other admins (only owners can)
if ($currentUserRole === 'admin' && $targetUserRole === 'admin') {
abort(403);
}

Expand All @@ -284,16 +309,24 @@ public function updateCollaboratorRole(Request $request, Mod $mod, User $collabo
{
$user = Auth::user();

if (!$mod->userCan($user, 'manage_collaborators')) {
if (! $mod->userCan($user, 'manage_collaborators')) {
abort(403);
}

$validated = $request->validate([
'role' => 'required|in:admin,editor,viewer',
]);

$currentUserRole = $mod->getUserRole($user);
$targetRole = $validated['role'];

// Only owners can promote to admin
if ($targetRole === 'admin' && $currentUserRole !== 'owner') {
abort(403);
}

$mod->collaborators()->updateExistingPivot($collaborator->id, [
'role' => $validated['role']
'role' => $validated['role'],
]);

return back()->with('success', 'Collaborator role updated successfully!');
Expand All @@ -312,7 +345,7 @@ public function publicShow($slug)
$rootPages = $mod->pages()
->whereNull('parent_id')
->where('published', true)
->with(['children' => function($query) {
->with(['children' => function ($query) {
$query->where('published', true)->orderBy('order_index');
}])
->orderBy('order_index')
Expand All @@ -325,15 +358,15 @@ public function publicShow($slug)

return Inertia::render('Public/Mod', [
'mod' => array_merge($mod->toArray(), [
'root_pages' => $rootPages->map(function($page) {
'root_pages' => $rootPages->map(function ($page) {
return [
'id' => $page->id,
'title' => $page->title,
'slug' => $page->slug,
'content' => substr($page->content ?? '', 0, 200),
'published' => $page->published,
'updated_at' => $page->updated_at,
'children' => $page->children->map(function($child) {
'children' => $page->children->map(function ($child) {
return [
'id' => $child->id,
'title' => $child->title,
Expand Down Expand Up @@ -375,10 +408,10 @@ public function showInvitation(string $token)
}

$user = Auth::user();
if (!$user || $user->id !== $invitation->user_id) {
if (! $user || $user->id !== $invitation->user_id) {
return Inertia::render('Invitations/Login', [
'invitation' => $invitation,
'needsLogin' => !$user,
'needsLogin' => ! $user,
'wrongUser' => $user && $user->id !== $invitation->user_id,
]);
}
Expand All @@ -399,7 +432,7 @@ public function acceptInvitation(string $token)
->where('token', $token)
->firstOrFail();

if (!$user || $user->id !== $invitation->user_id) {
if (! $user || $user->id !== $invitation->user_id) {
return redirect()->route('login')
->with('error', 'Please login to accept this invitation.');
}
Expand Down
Loading
Loading