first commit
This commit is contained in:
40
app/Events/DocumentVersionUpdated.php
Normal file
40
app/Events/DocumentVersionUpdated.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Document;
|
||||
use Illuminate\Broadcasting\Channel;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class DocumentVersionUpdated
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public $document;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
|
||||
public function __construct(Document $document)
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
*
|
||||
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||
*/
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [
|
||||
new PrivateChannel('channel-name'),
|
||||
];
|
||||
}
|
||||
}
|
||||
66
app/Http/Controllers/ActivityLogController.php
Normal file
66
app/Http/Controllers/ActivityLogController.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\ActivityLog;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ActivityLogController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$logs = ActivityLog::latest()->paginate(10);
|
||||
return view('activity_log.index', compact('logs'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(ActivityLog $activityLog)
|
||||
{
|
||||
return view('activity_log.show', compact('activityLog'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(ActivityLog $activityLog)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, ActivityLog $activityLog)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(ActivityLog $activityLog)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/ApprovalController.php
Normal file
30
app/Http/Controllers/ApprovalController.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Notifications\DocumentStatusChanged;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ApprovalController extends Controller
|
||||
{
|
||||
//
|
||||
|
||||
public function updateStatus(Request $request, Document $document)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|in:approved,rejected',
|
||||
'comment' => 'required_if:status,rejected'
|
||||
]);
|
||||
|
||||
$document->approvals()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'status' => $validated['status'],
|
||||
'comment' => $validated['comment'] ?? null
|
||||
]);
|
||||
|
||||
$document->update(['status' => $validated['status']]);
|
||||
|
||||
event(new DocumentStatusChanged($document, $validated['status']));
|
||||
}
|
||||
}
|
||||
85
app/Http/Controllers/ApprovalWorkflowController.php
Normal file
85
app/Http/Controllers/ApprovalWorkflowController.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\ApprovalWorkflow;
|
||||
use App\Models\Document;
|
||||
use App\Notifications\ApprovalRequestNotification;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class ApprovalWorkflowController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(ApprovalWorkflow $approvalWorkflow)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(ApprovalWorkflow $approvalWorkflow)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, ApprovalWorkflow $approvalWorkflow)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(ApprovalWorkflow $approvalWorkflow)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function initiateApproval(Document $document)
|
||||
{
|
||||
$workflow = $document->project->approvalWorkflow;
|
||||
$currentStep = $workflow->getCurrentStep($document);
|
||||
|
||||
$document->approvals()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'status' => 'pending',
|
||||
'step_index' => 0,
|
||||
'required_role' => $currentStep['role']
|
||||
]);
|
||||
|
||||
Notification::sendUsersWithRole($currentStep['role'])->notify(
|
||||
new ApprovalRequestNotification($document, $auth()->user())
|
||||
);
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
30
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class VerifyEmailController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mark the authenticated user's email address as verified.
|
||||
*/
|
||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
|
||||
if ($request->user()->markEmailAsVerified()) {
|
||||
/** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
|
||||
$user = $request->user();
|
||||
|
||||
event(new Verified($user));
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
}
|
||||
35
app/Http/Controllers/CommentController.php
Normal file
35
app/Http/Controllers/CommentController.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CommentController extends Controller
|
||||
{
|
||||
public function store(Request $request, Document $document)
|
||||
{
|
||||
$comment = $document->comments()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'content' => $request->content,
|
||||
'parent_id' => $request->parent_id
|
||||
]);
|
||||
|
||||
$this->processMentions($comment);
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
private function processMentions(Comment $comment)
|
||||
{
|
||||
preg_match_all('/@([\w\-]+)/', $comment->content, $matches);
|
||||
|
||||
foreach ($matches[1] as $username) {
|
||||
$user = User::where('username', $username)->first();
|
||||
if ($user) {
|
||||
$user->notify(new MentionNotification($comment));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
67
app/Http/Controllers/DashboardController.php
Normal file
67
app/Http/Controllers/DashboardController.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Project;
|
||||
use App\Models\Document;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
// Estadísticas principales
|
||||
$stats = [
|
||||
'projects_count' => Project::count(),
|
||||
'documents_count' => Document::count(),
|
||||
'users_count' => User::count(),
|
||||
'storage_used' => $this->calculateStorageUsed(),
|
||||
'storage_limit' => 0,
|
||||
'storage_percentage' => 0,
|
||||
];
|
||||
|
||||
// Documentos recientes (últimos 7 días)
|
||||
$recentDocuments = Document::with(['project', 'currentVersion'])
|
||||
->where('created_at', '>=', now()->subDays(7))
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit(5)
|
||||
->get();
|
||||
|
||||
// Actividad reciente
|
||||
$recentActivities = DB::table('activity_log')
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
return view('dashboard', compact('stats', 'recentDocuments', 'recentActivities'));
|
||||
}
|
||||
|
||||
private function calculateStorageUsed()
|
||||
{
|
||||
return Document::with('versions')
|
||||
->get()
|
||||
->sum(function($document) {
|
||||
return $document->versions->sum('size');
|
||||
});
|
||||
}
|
||||
|
||||
public function storageUsage()
|
||||
{
|
||||
$total = $this->calculateStorageUsed();
|
||||
$limit = config('app.storage_limit', 1073741824); // 1GB por defecto
|
||||
|
||||
return response()->json([
|
||||
'used' => $total,
|
||||
'limit' => $limit,
|
||||
'percentage' => ($total / $limit) * 100
|
||||
]);
|
||||
}
|
||||
|
||||
private function calculateStorage($projects)
|
||||
{
|
||||
// Adaptación de tu lógica existente + nueva propuesta
|
||||
return $projects->sum('storage_used') . ' GB';
|
||||
}
|
||||
}
|
||||
112
app/Http/Controllers/DocumentController.php
Normal file
112
app/Http/Controllers/DocumentController.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Jobs\ProcessDocumentOCR;
|
||||
use App\Models\Document;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DocumentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'files.*' => 'required|file|mimes:pdf,docx,xlsx,jpg,png|max:5120',
|
||||
'project_id' => 'required|exists:projects,id',
|
||||
'folder_id' => 'nullable|exists:folders,id'
|
||||
]);
|
||||
|
||||
foreach ($request->file('files') as $file) {
|
||||
$document = Document::create([
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'project_id' => $request->project_id,
|
||||
'folder_id' => $request->folder_id,
|
||||
'created_by' => auth()->id()
|
||||
]);
|
||||
|
||||
$document->addMedia($file)->toMediaCollection('documents');
|
||||
ProcessDocumentOCR::dispatch($document->currentVersion);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', 'Files uploaded successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Document $document)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Document $document)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Document $document)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Document $document)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function upload(Request $request, Project $project)
|
||||
{
|
||||
$request->validate([
|
||||
'files.*' => 'required|mimes:pdf,docx,xlsx,jpeg,png|max:2048'
|
||||
]);
|
||||
|
||||
foreach ($request->file('files') as $file) {
|
||||
$document = $project->documents()->create([
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'status' => 'pending'
|
||||
]);
|
||||
|
||||
$this->createVersion($document, $file);
|
||||
}
|
||||
}
|
||||
|
||||
private function createVersion(Document $document, $file)
|
||||
{
|
||||
$version = $document->versions()->create([
|
||||
'file_path' => $file->store("projects/{$document->project_id}/documents"),
|
||||
'hash' => hash_file('sha256', $file),
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
|
||||
$document->update(['current_version_id' => $version->id]);
|
||||
}
|
||||
}
|
||||
65
app/Http/Controllers/FolderController.php
Normal file
65
app/Http/Controllers/FolderController.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Folder;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FolderController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Folder $folder)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Folder $folder)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Folder $folder)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Folder $folder)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
56
app/Http/Controllers/PermissionController.php
Normal file
56
app/Http/Controllers/PermissionController.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class PermissionController extends Controller
|
||||
{
|
||||
use AuthorizesRequests; // <-- Añade este trait
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('viewAny', Permission::class);
|
||||
$permissions = Permission::all()->groupBy('group');
|
||||
return view('permissions.index', compact('permissions'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', Permission::class);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255|unique:permissions,name',
|
||||
'group' => 'required|string|max:255'
|
||||
]);
|
||||
|
||||
Permission::create($request->only('name', 'group'));
|
||||
|
||||
return redirect()->route('permissions.index')
|
||||
->with('success', 'Permiso creado exitosamente');
|
||||
}
|
||||
|
||||
public function update(Request $request, Permission $permission)
|
||||
{
|
||||
$this->authorize('update', $permission);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255|unique:permissions,name,'.$permission->id,
|
||||
'group' => 'required|string|max:255'
|
||||
]);
|
||||
|
||||
$permission->update($request->only('name', 'group'));
|
||||
|
||||
return redirect()->route('permissions.index')
|
||||
->with('success', 'Permiso actualizado correctamente');
|
||||
}
|
||||
|
||||
public function updateRoles(User $user, Request $request)
|
||||
{
|
||||
$this->authorize('managePermissions', $user);
|
||||
// Usar UserPolicy para autorizar
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/PreviewController.php
Normal file
27
app/Http/Controllers/PreviewController.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\DocumentVersion;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PreviewController extends Controller
|
||||
{
|
||||
public function show(DocumentVersion $version)
|
||||
{
|
||||
$filePath = storage_path("app/{$version->file_path}");
|
||||
|
||||
return match($version->mime_type) {
|
||||
'application/pdf' => response()->file($filePath),
|
||||
'image/*' => response()->file($filePath),
|
||||
default => response()->file(
|
||||
$this->convertToPdf($filePath)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private function convertToPdf($filePath)
|
||||
{
|
||||
// Usar OnlyOffice o LibreOffice para conversión
|
||||
}
|
||||
}
|
||||
50
app/Http/Controllers/ProfileController.php
Normal file
50
app/Http/Controllers/ProfileController.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function edit()
|
||||
{
|
||||
$user = Auth::user();
|
||||
return view('profile.edit', compact('user'));
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
|
||||
'current_password' => ['nullable', 'required_with:password', 'current_password'],
|
||||
'password' => ['nullable', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
$user->update([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
]);
|
||||
|
||||
if ($request->filled('password')) {
|
||||
$user->update([
|
||||
'password' => Hash::make($request->password)
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('profile.edit')
|
||||
->with('status', 'Perfil actualizado correctamente');
|
||||
}
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
return view('profile.show', [
|
||||
'user' => $request->user()
|
||||
]);
|
||||
}
|
||||
}
|
||||
158
app/Http/Controllers/ProjectController.php
Normal file
158
app/Http/Controllers/ProjectController.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
use AuthorizesRequests; // ← Añadir este trait
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = Project::withCount('documents')
|
||||
->whereHas('users', function($query) {
|
||||
$query->where('user_id', auth()->id());
|
||||
})
|
||||
->filter(['search' => request('search')])
|
||||
->paginate(9);
|
||||
|
||||
return view('projects.index', compact('projects'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create projects');
|
||||
|
||||
return view('projects.create', [
|
||||
'categories' => Category::orderBy('name')->get(),
|
||||
'users' => User::where('id', '!=', auth()->id())->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'description' => 'required|string',
|
||||
'status' => 'required|in:active,inactive',
|
||||
'team' => 'sometimes|array',
|
||||
'team.*' => 'exists:users,id',
|
||||
'project_image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'province' => 'nullable|string|max:100',
|
||||
'country' => 'nullable|string|size:2',
|
||||
'postal_code' => 'nullable|string|max:10',
|
||||
'latitude' => 'required|numeric|between:-90,90',
|
||||
'longitude' => 'required|numeric|between:-180,180',
|
||||
'icon' => 'nullable|in:'.implode(',', config('project.icons')),
|
||||
'start_date' => 'nullable|date',
|
||||
'deadline' => 'nullable|date|after:start_date',
|
||||
'categories' => 'array|exists:categories,id',
|
||||
//'categories' => 'required|array',
|
||||
'categories.*' => 'exists:categories,id',
|
||||
'documents.*' => 'file|max:5120|mimes:pdf,docx,xlsx,jpg,png'
|
||||
]);
|
||||
|
||||
|
||||
try {
|
||||
// Combinar datos del usuario autenticado
|
||||
$validated = array_merge($validated, [
|
||||
'creator_id' => auth()->id()
|
||||
]);
|
||||
|
||||
// Manejar la imagen
|
||||
if ($request->hasFile('project_image')) {
|
||||
$path = $request->file('project_image')->store('project-images', 'public');
|
||||
$validated['project_image_path'] = $path; // Usar el nombre correcto de columna
|
||||
}
|
||||
|
||||
// Crear el proyecto con todos los datos validados
|
||||
$project = Project::create($validated);
|
||||
|
||||
// Adjuntar categorías
|
||||
if($request->has('categories')) {
|
||||
$project->categories()->sync($request->categories);
|
||||
}
|
||||
|
||||
// Manejar documentos adjuntos
|
||||
if($request->hasFile('documents')) {
|
||||
foreach ($request->file('documents') as $file) {
|
||||
$project->documents()->create([
|
||||
'file_path' => $file->store('project-documents', 'public'),
|
||||
'original_name' => $file->getClientOriginalName()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('projects.show', $project)
|
||||
->with('success', 'Proyecto creado exitosamente');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withInput()
|
||||
->with('error', 'Error al crear el proyecto: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Project $project)
|
||||
{
|
||||
$this->authorize('view', $project); // Si usas políticas
|
||||
$project->load(['categories', 'documents']);
|
||||
|
||||
return view('projects.show', [
|
||||
'project' => $project->load(['rootFolders', 'documents', 'categories']),
|
||||
'documents' => $project->documents()->paginate(10),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Project $project)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Project $project)
|
||||
{
|
||||
$this->authorize('update', $project);
|
||||
// Lógica de actualización
|
||||
$project->update($request->all());
|
||||
return redirect()->route('projects.show', $project)->with('success', 'Project updated successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Project $project)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function __invoke(Project $project)
|
||||
{
|
||||
return view('projects.show', [
|
||||
'project' => $project
|
||||
]);
|
||||
}
|
||||
}
|
||||
78
app/Http/Controllers/RoleController.php
Normal file
78
app/Http/Controllers/RoleController.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use App\Http\Requests\StoreRoleRequest;
|
||||
|
||||
class RoleController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('viewAny', Role::class);
|
||||
$roles = Role::withCount('users')->paginate(10);
|
||||
return view('roles.index', compact('roles'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Role::class);
|
||||
$permissions = Permission::all()->groupBy('group');
|
||||
return view('roles.create', compact('permissions'));
|
||||
}
|
||||
|
||||
public function store(StoreRoleRequest $request)
|
||||
{
|
||||
$role = Role::create($request->only('name'));
|
||||
$role->syncPermissions($request->permissions);
|
||||
|
||||
return redirect()->route('roles.index')
|
||||
->with('success', 'Rol creado exitosamente');
|
||||
}
|
||||
|
||||
public function edit(Role $role)
|
||||
{
|
||||
$this->authorize('update', $role);
|
||||
$permissions = Permission::all()->groupBy('group');
|
||||
$rolePermissions = $role->permissions->pluck('id')->toArray();
|
||||
|
||||
return view('roles.edit', compact('role', 'permissions', 'rolePermissions'));
|
||||
}
|
||||
|
||||
public function update(StoreRoleRequest $request, Role $role)
|
||||
{
|
||||
$role->update($request->only('name'));
|
||||
$role->syncPermissions($request->permissions);
|
||||
|
||||
return redirect()->route('roles.index')
|
||||
->with('success', 'Rol actualizado correctamente');
|
||||
}
|
||||
|
||||
public function destroy(Role $role)
|
||||
{
|
||||
$this->authorize('delete', $role);
|
||||
|
||||
if($role->is_protected) {
|
||||
return redirect()->back()
|
||||
->with('error', 'No se puede eliminar un rol protegido');
|
||||
}
|
||||
|
||||
$role->delete();
|
||||
return redirect()->route('roles.index')
|
||||
->with('success', 'Rol eliminado correctamente');
|
||||
}
|
||||
|
||||
public function syncPermissions(Request $request, Role $role)
|
||||
{
|
||||
$this->authorize('update', $role);
|
||||
$request->validate([
|
||||
'permissions' => 'required|array',
|
||||
'permissions.*' => 'exists:permissions,id'
|
||||
]);
|
||||
|
||||
$role->syncPermissions($request->permissions);
|
||||
return response()->json(['message' => 'Permisos actualizados correctamente']);
|
||||
}
|
||||
}
|
||||
97
app/Http/Controllers/UserController.php
Normal file
97
app/Http/Controllers/UserController.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Http\Requests\UpdateUserRequest;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('viewAny', User::class);
|
||||
$users = User::with('roles')->paginate(10);
|
||||
return view('users.index', compact('users'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
$roles = Role::all();
|
||||
return view('users.create', compact('roles'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$data = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|unique:users',
|
||||
'password' => 'required|min:8|confirmed',
|
||||
'roles' => 'array'
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make($data['password'])
|
||||
]);
|
||||
|
||||
$user->syncRoles($data['roles'] ?? []);
|
||||
|
||||
return redirect()->route('users.index')
|
||||
->with('success', 'Usuario creado exitosamente');
|
||||
}
|
||||
|
||||
public function edit(User $user)
|
||||
{
|
||||
$this->authorize('update', $user);
|
||||
$roles = Role::all();
|
||||
$userRoles = $user->roles->pluck('id')->toArray();
|
||||
|
||||
return view('users.edit', compact('user', 'roles', 'userRoles'));
|
||||
}
|
||||
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
{
|
||||
$user->update($request->validated());
|
||||
$user->syncRoles($request->roles);
|
||||
|
||||
return redirect()->route('users.index')
|
||||
->with('success', 'Usuario actualizado correctamente');
|
||||
}
|
||||
|
||||
public function updatePassword(Request $request, User $user)
|
||||
{
|
||||
$this->authorize('update', $user);
|
||||
|
||||
$request->validate([
|
||||
'password' => 'required|min:8|confirmed'
|
||||
]);
|
||||
|
||||
$user->update([
|
||||
'password' => Hash::make($request->password)
|
||||
]);
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', 'Contraseña actualizada correctamente');
|
||||
}
|
||||
|
||||
public function destroy(User $user)
|
||||
{
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
if($user->is_protected) {
|
||||
return redirect()->back()
|
||||
->with('error', 'No se puede eliminar un usuario protegido');
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
return redirect()->route('users.index')
|
||||
->with('success', 'Usuario eliminado correctamente');
|
||||
}
|
||||
}
|
||||
31
app/Jobs/ProcessDocumentOCR.php
Normal file
31
app/Jobs/ProcessDocumentOCR.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use thiagoalessio\TesseractOCR\TesseractOCR;
|
||||
|
||||
class ProcessDocumentOCR implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(public DocumentVersion $version)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$ocr = new TesseractOCR();
|
||||
$text = $ocr->file(storage_path("app/{$this->version->file_path}"))->run();
|
||||
|
||||
$this->version->update(['ocr_text' => $text]);
|
||||
}
|
||||
}
|
||||
26
app/Jobs/ProcessDocumentUpload.php
Normal file
26
app/Jobs/ProcessDocumentUpload.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
|
||||
class ProcessDocumentUpload implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(public Document $document, public UploadedFile $file)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
// Lógica para procesar el archivo
|
||||
$this->document->createVersion($this->file);
|
||||
|
||||
// Generar miniaturas si es imagen
|
||||
if (Str::startsWith($this->file->getMimeType(), 'image/')) {
|
||||
$this->document->generateThumbnails();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
app/Listeners/SendDocumentVersionNotification.php
Normal file
39
app/Listeners/SendDocumentVersionNotification.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\DocumentVersionUpdated;
|
||||
use App\Models\User;
|
||||
use App\Notifications\DocumentUpdatedNotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
class SendDocumentVersionNotification
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*/
|
||||
public function handle(DocumentVersionUpdated $event): void
|
||||
{
|
||||
// Notificar a usuarios relevantes
|
||||
$users = User::whereHas('roles', function($query) {
|
||||
$query->whereIn('name', ['admin', 'approver']);
|
||||
})
|
||||
->orWhereHas('projects', function($query) use ($event) {
|
||||
$query->where('id', $event->document->project_id);
|
||||
})
|
||||
->get();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$user->notify(new DocumentUpdatedNotification($event->document));
|
||||
}
|
||||
}
|
||||
}
|
||||
22
app/Livewire/Actions/Logout.php
Normal file
22
app/Livewire/Actions/Logout.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Actions;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class Logout
|
||||
{
|
||||
/**
|
||||
* Log the current user out of the application.
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
Session::invalidate();
|
||||
Session::regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
13
app/Livewire/ApprovalWorkflow.php
Normal file
13
app/Livewire/ApprovalWorkflow.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class ApprovalWorkflow extends Component
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.approval-workflow');
|
||||
}
|
||||
}
|
||||
13
app/Livewire/CommentSystem.php
Normal file
13
app/Livewire/CommentSystem.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class CommentSystem extends Component
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.comment-system');
|
||||
}
|
||||
}
|
||||
13
app/Livewire/DocumentBrowser.php
Normal file
13
app/Livewire/DocumentBrowser.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class DocumentBrowser extends Component
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.document-browser');
|
||||
}
|
||||
}
|
||||
55
app/Livewire/Folder/CreateModal.php
Normal file
55
app/Livewire/Folder/CreateModal.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Folder;
|
||||
|
||||
use Livewire\Component;
|
||||
use App\Models\Folder;
|
||||
|
||||
class CreateModal extends Component
|
||||
{
|
||||
|
||||
public $project;
|
||||
public $parentFolder;
|
||||
public $folderName = '';
|
||||
public $showModal = false;
|
||||
|
||||
protected $listeners = [
|
||||
'openCreateFolderModal' => 'openForRoot',
|
||||
'openCreateSubfolderModal' => 'openForParent'
|
||||
];
|
||||
|
||||
public function openForRoot($projectId)
|
||||
{
|
||||
$this->project = Project::find($projectId);
|
||||
$this->parentFolder = null;
|
||||
$this->showModal = true;
|
||||
}
|
||||
|
||||
public function openForParent($parentFolderId)
|
||||
{
|
||||
$this->parentFolder = Folder::find($parentFolderId);
|
||||
$this->project = $this->parentFolder->project;
|
||||
$this->showModal = true;
|
||||
}
|
||||
|
||||
public function createFolder()
|
||||
{
|
||||
$this->validate([
|
||||
'folderName' => 'required|max:255|unique:folders,name'
|
||||
]);
|
||||
|
||||
Folder::create([
|
||||
'name' => $this->folderName,
|
||||
'project_id' => $this->project->id,
|
||||
'parent_id' => $this->parentFolder?->id
|
||||
]);
|
||||
|
||||
$this->reset(['folderName', 'showModal']);
|
||||
$this->emit('folderCreated');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.folder.create-modal');
|
||||
}
|
||||
}
|
||||
50
app/Livewire/ProjectShow.php
Normal file
50
app/Livewire/ProjectShow.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use App\Models\Project;
|
||||
use App\Models\Folder;
|
||||
use App\Models\Document;
|
||||
|
||||
class ProjectShow extends Component
|
||||
{
|
||||
|
||||
public Project $project;
|
||||
public $selectedFolderId = null;
|
||||
public $expandedFolders = [];
|
||||
|
||||
public function mount(Project $project)
|
||||
{
|
||||
$this->project = $project->load('rootFolders');
|
||||
}
|
||||
|
||||
public function selectFolder($folderId)
|
||||
{
|
||||
$this->selectedFolderId = $folderId;
|
||||
}
|
||||
|
||||
public function toggleFolder($folderId)
|
||||
{
|
||||
if (in_array($folderId, $this->expandedFolders)) {
|
||||
$this->expandedFolders = array_diff($this->expandedFolders, [$folderId]);
|
||||
} else {
|
||||
$this->expandedFolders[] = $folderId;
|
||||
}
|
||||
}
|
||||
|
||||
public function getDocumentsProperty()
|
||||
{
|
||||
return Document::where('folder_id', $this->selectedFolderId)
|
||||
->where('project_id', $this->project->id)
|
||||
->with('versions')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project-show', [
|
||||
'rootFolders' => $this->project->rootFolders
|
||||
]);
|
||||
}
|
||||
}
|
||||
31
app/Models/ActivityLog.php
Normal file
31
app/Models/ActivityLog.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ActivityLog extends Model
|
||||
{
|
||||
protected $table = 'activity_log';
|
||||
|
||||
protected $fillable = [
|
||||
'log_name',
|
||||
'description',
|
||||
'subject_id',
|
||||
'subject_type',
|
||||
'causer_id',
|
||||
'causer_type',
|
||||
'properties'
|
||||
];
|
||||
|
||||
public function subject()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function causer()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
}
|
||||
24
app/Models/Approval.php
Normal file
24
app/Models/Approval.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Approval extends Model
|
||||
{
|
||||
protected $casts = [
|
||||
'metadata' => 'array',
|
||||
'step' => ApprovalStep::class
|
||||
];
|
||||
|
||||
public function transitionTo($status, $comment = null)
|
||||
{
|
||||
$this->update([
|
||||
'status' => $status,
|
||||
'comment' => $comment,
|
||||
'completed_at' => now()
|
||||
]);
|
||||
|
||||
$this->document->notifyApprovers();
|
||||
}
|
||||
}
|
||||
16
app/Models/ApprovalWorkflow.php
Normal file
16
app/Models/ApprovalWorkflow.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ApprovalWorkflow extends Model
|
||||
{
|
||||
protected $casts = ['steps' => 'array'];
|
||||
|
||||
public function getCurrentStep(Document $document)
|
||||
{
|
||||
$lastApproval = $document->approvals()->latest()->first();
|
||||
return $lastApproval ? $this->steps[$lastApproval->step_index + 1] : $this->steps[0];
|
||||
}
|
||||
}
|
||||
17
app/Models/Category.php
Normal file
17
app/Models/Category.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Category extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'slug'];
|
||||
|
||||
|
||||
public function projects(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Project::class);
|
||||
}
|
||||
}
|
||||
75
app/Models/Document.php
Normal file
75
app/Models/Document.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Events\DocumentVersionUpdated;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
|
||||
class Document extends Model
|
||||
{
|
||||
|
||||
use LogsActivity;
|
||||
|
||||
protected static $logAttributes = ['name', 'status'];
|
||||
protected static $logOnlyDirty = true;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'status',
|
||||
'project_id',
|
||||
'folder_id',
|
||||
'current_version_id'
|
||||
];
|
||||
|
||||
|
||||
public function versions() {
|
||||
return $this->hasMany(DocumentVersion::class);
|
||||
}
|
||||
|
||||
public function approvals() {
|
||||
return $this->hasMany(Approval::class);
|
||||
}
|
||||
|
||||
public function comments() {
|
||||
return $this->hasMany(Comment::class)->whereNull('parent_id');
|
||||
}
|
||||
|
||||
public function createVersion($file)
|
||||
{
|
||||
return $this->versions()->create([
|
||||
'file_path' => $file->store("documents/{$this->id}/versions"),
|
||||
'hash' => hash_file('sha256', $file),
|
||||
'user_id' => auth()->id(),
|
||||
'version_number' => $this->versions()->count() + 1
|
||||
]);
|
||||
}
|
||||
|
||||
public function getCurrentVersionAttribute()
|
||||
{
|
||||
return $this->versions()->latest()->first();
|
||||
}
|
||||
|
||||
public function uploadVersion($file)
|
||||
{
|
||||
$this->versions()->create([
|
||||
'file_path' => $file->store("projects/{$this->id}/versions"),
|
||||
'hash' => hash_file('sha256', $file),
|
||||
'version' => $this->versions()->count() + 1,
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
|
||||
event(new DocumentVersionUpdated($this));
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->logExcept(['current_version_id'])
|
||||
->logUnguarded();
|
||||
}
|
||||
|
||||
}
|
||||
10
app/Models/DocumentVersion.php
Normal file
10
app/Models/DocumentVersion.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DocumentVersion extends Model
|
||||
{
|
||||
//
|
||||
}
|
||||
43
app/Models/Folder.php
Normal file
43
app/Models/Folder.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Folder extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'parent_id',
|
||||
'project_id',
|
||||
'icon',
|
||||
'color',
|
||||
'description',
|
||||
];
|
||||
|
||||
public function descendants()
|
||||
{
|
||||
return $this->belongsToMany(Folder::class, 'folder_closure', 'ancestor_id', 'descendant_id')
|
||||
->withPivot('depth');
|
||||
}
|
||||
|
||||
public function documents()
|
||||
{
|
||||
return $this->hasMany(Document::class);
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(Folder::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(Folder::class, 'parent_id')->with('children');
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
}
|
||||
80
app/Models/Project.php
Normal file
80
app/Models/Project.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Project extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'creator_id',
|
||||
'status',
|
||||
'project_image_path',
|
||||
'address',
|
||||
'province',
|
||||
'country',
|
||||
'postal_code',
|
||||
'latitude',
|
||||
'longitude',
|
||||
'icon',
|
||||
'start_date',
|
||||
'deadline',
|
||||
// Agrega cualquier otro campo nuevo aquí
|
||||
];
|
||||
|
||||
public function folders() {
|
||||
return $this->hasMany(Folder::class);
|
||||
}
|
||||
|
||||
public function documents() {
|
||||
return $this->hasMany(Document::class);
|
||||
}
|
||||
|
||||
public function rootFolders()
|
||||
{
|
||||
return $this->folders()->whereNull('parent_id')->with('children');
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function managers()
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'project_managers');
|
||||
}
|
||||
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'project_users');
|
||||
}
|
||||
|
||||
|
||||
public function scopeFilter(Builder $query, array $filters)
|
||||
{
|
||||
$query->when($filters['search'] ?? false, function($query, $search) {
|
||||
$query->where(function($query) use ($search) {
|
||||
$query->where('name', 'like', "%{$search}%")
|
||||
->orWhere('description', 'like', "%{$search}%");
|
||||
});
|
||||
});
|
||||
|
||||
// Agrega más filtros según necesites
|
||||
/*
|
||||
$query->when($filters['status'] ?? false, function($query, $status) {
|
||||
$query->where('status', $status);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
public function categories()
|
||||
{
|
||||
return $this->belongsToMany(Category::class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
63
app/Models/User.php
Normal file
63
app/Models/User.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
use HasRoles;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'is_protected' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's initials
|
||||
*/
|
||||
public function initials(): string
|
||||
{
|
||||
return Str::of($this->name)
|
||||
->explode(' ')
|
||||
->map(fn (string $name) => Str::of($name)->substr(0, 1))
|
||||
->implode('');
|
||||
}
|
||||
}
|
||||
68
app/Notifications/ApprovalRequestNotification.php
Normal file
68
app/Notifications/ApprovalRequestNotification.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class ApprovalRequestNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
protected $document;
|
||||
protected $requester;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct(Document $document, User $requester)
|
||||
{
|
||||
$this->document = $document;
|
||||
$this->requester = $requester;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['mail', 'database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
public function toMail(object $notifiable): MailMessage
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject('Solicitud de aprobación de documento')
|
||||
->greeting('Hola ' . $notifiable->name . '!')
|
||||
->line($this->requester->name . ' ha solicitado tu aprobación para el documento:')
|
||||
->line('**Documento:** ' . $this->document->name)
|
||||
->action('Revisar Documento', route('documents.show', $this->document))
|
||||
->line('Fecha límite: ' . $this->document->due_date->format('d/m/Y'))
|
||||
->line('Gracias por usar nuestro sistema!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
'type' => 'approval-request',
|
||||
'document_id' => $this->document->id,
|
||||
'requester_id' => $this->requester->id,
|
||||
'message' => 'Nueva solicitud de aprobación para: ' . $this->document->name,
|
||||
'link' => route('documents.show', $this->document)
|
||||
];
|
||||
}
|
||||
}
|
||||
54
app/Notifications/DocumentStatusChanged.php
Normal file
54
app/Notifications/DocumentStatusChanged.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class DocumentStatusChanged extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct(public Document $document, public string $action)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['database', 'mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
public function toMail(object $notifiable): MailMessage
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject("Document status changed: {$this->document->name}")
|
||||
->line("The document '{$this->document->name}' has been {$this->action}.")
|
||||
->action('View Document', route('documents.show', $this->document));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
60
app/Notifications/DocumentUpdatedNotification.php
Normal file
60
app/Notifications/DocumentUpdatedNotification.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Document;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class DocumentUpdatedNotification extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
protected $document;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct(Document $document)
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['mail', 'database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
public function toMail(object $notifiable): MailMessage
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject('Nueva versión de documento: ' . $this->document->name)
|
||||
->line('Se ha subido una nueva versión del documento.')
|
||||
->action('Ver Documento', route('documents.show', $this->document))
|
||||
->line('Gracias por usar nuestro sistema!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
'document_id' => $this->document->id,
|
||||
'message' => 'Nueva versión del documento: ' . $this->document->name,
|
||||
'url' => route('documents.show', $this->document)
|
||||
];
|
||||
}
|
||||
}
|
||||
21
app/Policies/DashboardPolicy.php
Normal file
21
app/Policies/DashboardPolicy.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class DashboardPolicy
|
||||
{
|
||||
/**
|
||||
* Create a new policy instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function view(User $user)
|
||||
{
|
||||
return $user->hasPermissionTo('view dashboard');
|
||||
}
|
||||
}
|
||||
67
app/Policies/DocumentPolicy.php
Normal file
67
app/Policies/DocumentPolicy.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class DocumentPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Document $document)
|
||||
{
|
||||
return $user->hasPermissionTo('view documents')
|
||||
&& $user->hasProjectAccess($document->project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Document $document): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Document $document): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, Document $document): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, Document $document): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
73
app/Policies/PermissionPolicy.php
Normal file
73
app/Policies/PermissionPolicy.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class PermissionPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Permission $permission): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('create permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Permission $permission): bool
|
||||
{
|
||||
if($permission->is_system) return false;
|
||||
|
||||
return $user->hasPermissionTo('edit permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Permission $permission): bool
|
||||
{
|
||||
if($permission->is_system || $permission->roles()->exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->hasPermissionTo('delete permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, Permission $permission): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, Permission $permission): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage permissions');
|
||||
}
|
||||
}
|
||||
21
app/Policies/ProfilePolicy.php
Normal file
21
app/Policies/ProfilePolicy.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class ProfilePolicy
|
||||
{
|
||||
/**
|
||||
* Create a new policy instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function update(User $user)
|
||||
{
|
||||
return $user->is(auth()->user());
|
||||
}
|
||||
}
|
||||
78
app/Policies/ProjectPolicy.php
Normal file
78
app/Policies/ProjectPolicy.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class ProjectPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view projects');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view projects') &&
|
||||
$this->hasProjectAccess($user, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('create projects');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermissionTo('edit projects') &&
|
||||
$this->hasProjectAccess($user, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermissionTo('delete projects') &&
|
||||
$this->hasProjectAccess($user, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, Project $project): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, Project $project): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function hasProjectAccess(User $user, Project $project)
|
||||
{
|
||||
// Verificar si el usuario es creador, gestor o tiene acceso directo
|
||||
return $project->creator_id === $user->id ||
|
||||
$project->managers->contains($user->id) ||
|
||||
$project->users->contains($user->id);
|
||||
}
|
||||
|
||||
}
|
||||
66
app/Policies/RolePolicy.php
Normal file
66
app/Policies/RolePolicy.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class RolePolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Role $role): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage roles') && !$role->is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage roles') && !$role->is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, Role $role): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, Role $role): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
73
app/Policies/UserPolicy.php
Normal file
73
app/Policies/UserPolicy.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class UserPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, User $model): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, User $model): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users') && !$model->is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, User $model): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users')
|
||||
&& !$model->is_protected
|
||||
&& $user->id !== $model->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, User $model): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, User $model): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function managePermissions(User $user)
|
||||
{
|
||||
// recomendada: return $authUser->isAdmin() && !$targetUser->isSuperAdmin();
|
||||
return $user->hasRole('admin'); // Solo los admins pueden gestionar permisos
|
||||
}
|
||||
}
|
||||
51
app/Providers/AppServiceProvider.php
Normal file
51
app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Policies\DocumentPolicy;
|
||||
use App\Policies\PermissionPolicy;
|
||||
use App\Policies\ProfilePolicy;
|
||||
use App\Policies\ProjectPolicy;
|
||||
use App\Policies\RolePolicy;
|
||||
use App\Policies\UserPolicy;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Livewire;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
protected $policies = [
|
||||
User::class => UserPolicy::class,
|
||||
User::class => ProfilePolicy::class,
|
||||
Role::class => RolePolicy::class,
|
||||
Permission::class => PermissionPolicy::class,
|
||||
Document::class => DocumentPolicy::class,
|
||||
Project::class => ProjectPolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
Blade::componentNamespace('App\\View\\Components', 'icons');
|
||||
Blade::component('multiselect', \App\View\Components\Multiselect::class);
|
||||
|
||||
Livewire::component('project-show', \App\Http\Livewire\ProjectShow::class);
|
||||
}
|
||||
}
|
||||
38
app/Providers/EventServiceProvider.php
Normal file
38
app/Providers/EventServiceProvider.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
//use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
// Agrega tus eventos aquí
|
||||
\App\Events\DocumentVersionUpdated::class => [
|
||||
\App\Listeners\SendDocumentVersionNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
}
|
||||
28
app/Providers/VoltServiceProvider.php
Normal file
28
app/Providers/VoltServiceProvider.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
class VoltServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Volt::mount([
|
||||
config('livewire.view_path', resource_path('views/livewire')),
|
||||
resource_path('views/pages'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
29
app/View/Components/ActivityIcon.php
Normal file
29
app/View/Components/ActivityIcon.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class ActivityIcon extends Component
|
||||
{
|
||||
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.activity-icon');
|
||||
}
|
||||
}
|
||||
31
app/View/Components/FolderItem.php
Normal file
31
app/View/Components/FolderItem.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class FolderItem extends Component
|
||||
{
|
||||
|
||||
public $folder;
|
||||
public $level;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct($folder, $level = 0)
|
||||
{
|
||||
$this->folder = $folder;
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.folder-item');
|
||||
}
|
||||
}
|
||||
35
app/View/Components/Multiselect.php
Normal file
35
app/View/Components/Multiselect.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Multiselect extends Component
|
||||
{
|
||||
|
||||
public $options;
|
||||
public $selected;
|
||||
public $name;
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct($options = [], $selected = [], $name = 'multiselect', $id = null)
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->selected = $selected;
|
||||
$this->name = $name;
|
||||
$this->id = $id ?? 'multiselect-' . uniqid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.multiselect');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user