authorizeWrite('settings'); $tipologie = TipologiaDocumento::orderBy('ordine')->get(); $tipologieEventi = TipologiaEvento::orderBy('ordine')->get(); $ruoli = Ruolo::orderBy('ordine')->get(); $tags = Tag::orderBy('order_column')->get(); $appSettings = AppSetting::first(); $emailSettings = EmailSetting::first() ?? new EmailSetting(); $senderAccounts = SenderAccount::orderBy('email_address')->get(); $repositories = StorageRepository::orderBy('ordine')->get(); $googleDriveNewToken = session('google_drive_new_token'); $calendarioConnessioni = CalendarioConnessione::orderBy('ordine')->get(); return view('impostazioni.index', compact('tipologie', 'tipologieEventi', 'ruoli', 'tags', 'appSettings', 'emailSettings', 'senderAccounts', 'repositories', 'googleDriveNewToken', 'calendarioConnessioni')); } public function saveAppSettings(Request $request) { $this->authorizeWrite('settings'); $validated = $request->validate([ 'nome_applicazione' => 'nullable|string|max:100', 'nome_organizzazione' => 'nullable|string|max:255', 'footer_text' => 'nullable|string|max:255', 'app_version' => 'nullable|string|max:50', 'dashboard_welcome' => 'nullable|string|max:1000', 'show_version' => 'boolean', 'documenti_storage_disk' => 'nullable|string|in:local,public', 'documenti_storage_path' => 'nullable|string|max:255', ]); $settings = AppSetting::first() ?? new AppSetting(); $oldDisk = $settings->documenti_storage_disk ?? 'local'; $oldPath = $settings->documenti_storage_path ?? 'documenti'; $settings->nome_applicazione = $validated['nome_applicazione'] ?? $settings->nome_applicazione ?? ''; $settings->nome_organizzazione = $validated['nome_organizzazione'] ?? $settings->nome_organizzazione ?? ''; $settings->footer_text = $validated['footer_text'] ?? $settings->footer_text ?? ''; $settings->app_version = $validated['app_version'] ?? $settings->app_version ?? ''; $settings->dashboard_welcome = $validated['dashboard_welcome'] ?? $settings->dashboard_welcome ?? null; $settings->show_version = $request->boolean('show_version'); $settings->documenti_storage_disk = $validated['documenti_storage_disk'] ?? $settings->documenti_storage_disk ?? 'local'; $settings->documenti_storage_path = $validated['documenti_storage_path'] ?? $settings->documenti_storage_path ?? 'documenti'; $settings->save(); $newDisk = $settings->documenti_storage_disk; $newPath = $settings->documenti_storage_path; $pathChanged = $oldDisk !== $newDisk || $oldPath !== $newPath; if ($pathChanged) { try { Storage::disk($newDisk)->makeDirectory($newPath); } catch (\Exception $e) { Log::error('Creazione directory documenti fallita: ' . $e->getMessage()); } $fileCount = Documento::whereNull('repository_id')->count(); return redirect('/impostazioni#documenti') ->with('success', 'Percorso di archiviazione aggiornato. La nuova directory รจ stata creata.') ->with('path_changed', true) ->with('old_storage_disk', $oldDisk) ->with('old_storage_path', $oldPath) ->with('new_storage_disk', $newDisk) ->with('new_storage_path', $newPath) ->with('migration_count', $fileCount); } return redirect('/impostazioni#applicazione')->with('success', 'Impostazioni applicazione salvate.'); } public function migrateStoragePath(Request $request) { $this->authorizeWrite('settings'); $oldDisk = $request->input('old_storage_disk', 'local'); $oldPath = $request->input('old_storage_path', 'documenti'); $newDisk = $request->input('new_storage_disk', 'local'); $newPath = $request->input('new_storage_path', 'documenti'); $documents = Documento::whereNull('repository_id')->get(); $moved = 0; $errors = []; foreach ($documents as $doc) { $oldFilePath = $doc->file_path; $relativePath = $oldFilePath; if (str_starts_with($relativePath, $oldPath . '/') || $relativePath === $oldPath) { $relativePath = substr($relativePath, strlen($oldPath)); $relativePath = ltrim($relativePath, '/'); } $newFilePath = $newPath . ($relativePath ? '/' . $relativePath : ''); if ($newFilePath === $oldFilePath && $oldDisk === $newDisk) { continue; } try { if (!Storage::disk($oldDisk)->exists($oldFilePath)) { $errors[] = "File non trovato: {$oldFilePath}"; continue; } Storage::disk($newDisk)->writeStream( $newFilePath, Storage::disk($oldDisk)->readStream($oldFilePath) ); $doc->update([ 'file_path' => $newFilePath, 'storage_disk' => $newDisk, ]); Storage::disk($oldDisk)->delete($oldFilePath); $moved++; } catch (\Exception $e) { $errors[] = "Errore su {$oldFilePath}: " . $e->getMessage(); Log::error('Migrazione file fallita: ' . $e->getMessage(), [ 'doc_id' => $doc->id, 'old' => $oldFilePath, 'new' => $newFilePath, ]); } } $message = "Migrazione completata: {$moved} file spostati."; if (count($errors) > 0) { $message .= ' ' . count($errors) . ' errori. Controlla i log per i dettagli.'; } return redirect('/impostazioni#documenti') ->with('success', $message) ->with('migration_errors', count($errors) > 0 ? $errors : null); } public function tipologieStore(Request $request) { $this->authorizeWrite('settings'); $validated = $request->validate([ 'nome' => 'required|string|max:50|unique:tipologie_documenti,nome', 'descrizione' => 'nullable|string|max:255', ]); $maxOrdine = TipologiaDocumento::max('ordine') ?? 0; TipologiaDocumento::create([ 'nome' => $validated['nome'], 'descrizione' => $validated['descrizione'] ?? null, 'ordine' => $maxOrdine + 1, 'attiva' => true, ]); return redirect('/impostazioni#tipologie')->with('success', 'Tipologia aggiunta.'); } public function tipologieUpdate(Request $request, $id) { $this->authorizeWrite('settings'); $tipologia = TipologiaDocumento::findOrFail($id); $validated = $request->validate([ 'nome' => 'required|string|max:50|unique:tipologie_documenti,nome,' . $id, 'descrizione' => 'nullable|string|max:255', 'attiva' => 'boolean', ]); $tipologia->update($validated); return redirect('/impostazioni#tipologie')->with('success', 'Tipologia aggiornata.'); } public function tipologieDestroy($id) { $this->authorizeDelete('settings'); $tipologia = TipologiaDocumento::findOrFail($id); $count = $tipologia->documenti()->count(); if ($count > 0) { return redirect('/impostazioni#tipologie')->with('error', 'Impossibile eliminare: ' . $count . ' documenti associati.'); } $tipologia->delete(); return redirect('/impostazioni#tipologie')->with('success', 'Tipologia eliminata.'); } public function tipologieReorder(Request $request) { $this->authorizeWrite('settings'); $order = $request->input('order', []); foreach ($order as $index => $id) { TipologiaDocumento::where('id', $id)->update(['ordine' => $index]); } return response()->json(['success' => true]); } public function ruoliStore(Request $request) { $this->authorizeWrite('settings'); $validated = $request->validate([ 'nome' => 'required|string|max:50|unique:ruoli,nome', 'descrizione' => 'nullable|string|max:255', ]); $maxOrdine = Ruolo::max('ordine') ?? 0; Ruolo::create([ 'nome' => $validated['nome'], 'descrizione' => $validated['descrizione'] ?? null, 'ordine' => $maxOrdine + 1, 'attiva' => true, ]); return redirect('/impostazioni#ruoli')->with('success', 'Ruolo aggiunto.'); } public function ruoliUpdate(Request $request, $id) { $this->authorizeWrite('settings'); $ruolo = Ruolo::findOrFail($id); $validated = $request->validate([ 'nome' => 'required|string|max:50|unique:ruoli,nome,' . $id, 'descrizione' => 'nullable|string|max:255', 'attiva' => 'boolean', ]); $ruolo->update($validated); return redirect('/impostazioni#ruoli')->with('success', 'Ruolo aggiornato.'); } public function ruoliDestroy($id) { $this->authorizeDelete('settings'); $ruolo = Ruolo::findOrFail($id); $allRows = \DB::table('gruppo_individuo') ->whereNotNull('ruolo_ids') ->get(['id', 'ruolo_ids']); $count = 0; foreach ($allRows as $row) { $ids = json_decode($row->ruolo_ids, true); if (is_array($ids) && in_array((int) $id, $ids)) { $count++; } } if ($count > 0) { return redirect('/impostazioni#ruoli')->with('error', 'Impossibile eliminare: ' . $count . ' membri associati a questo ruolo.'); } $ruolo->delete(); return redirect('/impostazioni#ruoli')->with('success', 'Ruolo eliminato.'); } public function ruoliReorder(Request $request) { $this->authorizeWrite('settings'); $order = $request->input('order', []); foreach ($order as $index => $id) { Ruolo::where('id', $id)->update(['ordine' => $index]); } return response()->json(['success' => true]); } public function tipologieEventiStore(Request $request) { $this->authorizeWrite('settings'); $validated = $request->validate([ 'nome' => 'required|string|max:50|unique:tipologie_eventi,nome', 'descrizione' => 'nullable|string|max:255', ]); $maxOrdine = TipologiaEvento::max('ordine') ?? 0; TipologiaEvento::create([ 'nome' => $validated['nome'], 'descrizione' => $validated['descrizione'] ?? null, 'ordine' => $maxOrdine + 1, 'attiva' => true, ]); return redirect('/impostazioni#tipologie-eventi')->with('success', 'Tipologia evento aggiunta.'); } public function tipologieEventiUpdate(Request $request, $id) { $this->authorizeWrite('settings'); $tipologia = TipologiaEvento::findOrFail($id); $validated = $request->validate([ 'nome' => 'required|string|max:50|unique:tipologie_eventi,nome,' . $id, 'descrizione' => 'nullable|string|max:255', 'attiva' => 'boolean', ]); $tipologia->update($validated); return redirect('/impostazioni#tipologie-eventi')->with('success', 'Tipologia evento aggiornata.'); } public function tipologieEventiDestroy($id) { $this->authorizeDelete('settings'); $tipologia = TipologiaEvento::findOrFail($id); $count = $tipologia->eventi()->count(); if ($count > 0) { return redirect('/impostazioni#tipologie-eventi')->with('error', 'Impossibile eliminare: ' . $count . ' eventi associati.'); } $tipologia->delete(); return redirect('/impostazioni#tipologie-eventi')->with('success', 'Tipologia evento eliminata.'); } public function tipologieEventiReorder(Request $request) { $this->authorizeWrite('settings'); $order = $request->input('order', []); foreach ($order as $index => $id) { TipologiaEvento::where('id', $id)->update(['ordine' => $index]); } return response()->json(['success' => true]); } public function tagStore(Request $request) { $this->authorizeWrite('settings'); $validated = $request->validate([ 'name' => 'required|string|max:100|unique:tags,name', 'color' => 'nullable|string|max:7', ]); $maxOrdine = Tag::max('order_column') ?? 0; Tag::create([ 'name' => $validated['name'], 'slug' => str()->slug($validated['name']), 'color' => $validated['color'] ?? null, 'order_column' => $maxOrdine + 1, ]); return redirect('/impostazioni#tag')->with('success', 'Tag aggiunto.'); } public function tagUpdate(Request $request, $id) { $this->authorizeWrite('settings'); $tag = Tag::findOrFail($id); $validated = $request->validate([ 'name' => 'required|string|max:100|unique:tags,name,' . $id, 'color' => 'nullable|string|max:7', ]); $tag->update([ 'name' => $validated['name'], 'slug' => str()->slug($validated['name']), 'color' => $validated['color'] ?? null, ]); return redirect('/impostazioni#tag')->with('success', 'Tag aggiornato.'); } public function tagDestroy($id) { $this->authorizeDelete('settings'); $tag = Tag::findOrFail($id); $tag->delete(); return redirect('/impostazioni#tag')->with('success', 'Tag eliminato.'); } public function tagReorder(Request $request) { $this->authorizeWrite('settings'); $order = $request->input('order', []); foreach ($order as $index => $id) { Tag::where('id', $id)->update(['order_column' => $index]); } return response()->json(['success' => true]); } public function uploadLogo(Request $request) { $this->authorizeWrite('settings'); $request->validate([ 'logo' => 'nullable|image|mimes:png,jpg,jpeg,gif,svg|max:2048', 'logo_small' => 'nullable|image|mimes:png,jpg,jpeg,gif,svg|max:512', ]); $settings = AppSetting::first() ?? new AppSetting(); if ($request->hasFile('logo')) { $path = $request->file('logo')->store('logos', 'public'); $settings->logo_path = $path; } if ($request->hasFile('logo_small')) { $path = $request->file('logo_small')->store('logos', 'public'); $settings->logo_small_path = $path; } if ($settings->id) { $settings->save(); } else { $settings->save(); } $linkPath = public_path('storage'); if (!file_exists($linkPath) && !is_link($linkPath)) { try { symlink(storage_path('app/public'), $linkPath); } catch (\Throwable $e) { Log::warning('Impossibile creare symlink storage: ' . $e->getMessage()); } } return redirect('/impostazioni#logo')->with('success', 'Logo caricato con successo.'); } public function removeLogo(Request $request) { $this->authorizeWrite('settings'); $type = $request->input('type'); $settings = AppSetting::first(); if ($settings) { if ($type === 'logo') { if ($settings->logo_path) { Storage::disk('public')->delete($settings->logo_path); $settings->logo_path = null; } } elseif ($type === 'logo_small') { if ($settings->logo_small_path) { Storage::disk('public')->delete($settings->logo_small_path); $settings->logo_small_path = null; } } $settings->save(); } return response()->json(['success' => true]); } }