pre version 2

This commit is contained in:
2026-06-17 13:50:41 +02:00
parent c333bbce4e
commit 6909e21b67
26 changed files with 1213 additions and 136 deletions
@@ -2,11 +2,13 @@
namespace App\Http\Controllers\Admin;
use App\Enums\GoogleService;
use App\Http\Controllers\Controller;
use App\Models\EmailAttachment;
use App\Models\EmailFolder;
use App\Models\EmailSetting;
use App\Models\SenderAccount;
use App\Services\GoogleOAuthService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
@@ -19,7 +21,8 @@ class EmailSettingsController extends Controller
$this->authorizeWrite('settings');
$settings = EmailSetting::first() ?? new EmailSetting();
$senderAccounts = SenderAccount::orderBy('email_address')->get();
return view('admin.email-settings.index', compact('settings', 'senderAccounts'));
$googleStatus = app(GoogleOAuthService::class)->getConnectionStatus();
return view('admin.email-settings.index', compact('settings', 'senderAccounts', 'googleStatus'));
}
public function save(Request $request)
@@ -43,6 +46,8 @@ class EmailSettingsController extends Controller
'is_active' => 'boolean',
'signature' => 'nullable|string',
'signature_enabled' => 'boolean',
'auth_method' => 'nullable|in:password,oauth',
'google_oauth_connection_id' => 'nullable|integer|exists:google_oauth_connections,id',
]);
if (!empty($validated['imap_password'])) {
@@ -61,29 +66,17 @@ class EmailSettingsController extends Controller
$validated
);
// Verifica che i dati siano stati salvati
$verify = EmailSetting::find($saved->id);
if ($verify &&
$allGood = $verify &&
$verify->imap_host === $validated['imap_host'] &&
$verify->email_address === $validated['email_address']) {
$verify->email_address === $validated['email_address'];
$signatureMatch = !array_key_exists('signature', $validated) || $verify->signature === $validated['signature'];
$signatureEnabledMatch = !array_key_exists('signature_enabled', $validated) || (bool)$verify->signature_enabled === (bool)$validated['signature_enabled'];
if (!$signatureMatch) {
\Illuminate\Support\Facades\Log::warning('Signature mismatch after save', [
'expected' => $validated['signature'] ?? null,
'actual' => $verify->signature,
]);
}
if (!$signatureEnabledMatch) {
\Illuminate\Support\Facades\Log::warning('Signature enabled mismatch after save', [
'expected' => $validated['signature_enabled'] ?? null,
'actual' => $verify->signature_enabled,
]);
}
$allGood = $allGood &&
(!array_key_exists('signature', $validated) || $verify->signature === $validated['signature']) &&
(!array_key_exists('signature_enabled', $validated) || (bool)$verify->signature_enabled === (bool)$validated['signature_enabled']);
if ($allGood) {
return back()->with('success', 'Impostazioni email salvate e verificate nel database.');
} else {
return back()->with('error', 'Errore nel salvataggio: i dati non sono stati salvati correttamente.');
@@ -99,7 +92,6 @@ class EmailSettingsController extends Controller
return response()->json(['success' => false, 'message' => 'Configurazione non trovata.']);
}
// Verifica che i dati siano salvati
if (empty($settings->imap_host) || empty($settings->email_address)) {
return response()->json(['success' => false, 'message' => 'Prima salva le impostazioni.']);
}
@@ -129,41 +121,13 @@ class EmailSettingsController extends Controller
return response()->json(['success' => false, 'message' => 'Configurazione non trovata.']);
}
if (empty($settings->smtp_host)) {
$settings->smtp_host = 'smtp.gmail.com';
$settings->smtp_port = 587;
$settings->smtp_encryption = 'tls';
}
try {
$smtpUsername = $settings->smtp_username;
$smtpPassword = $settings->smtp_password;
if (empty($smtpUsername)) {
$smtpUsername = $settings->email_address;
}
if (empty($smtpPassword)) {
$smtpPassword = $settings->getDecryptedPassword();
} else {
$smtpPassword = Crypt::decryptString($smtpPassword);
$mailer = $settings->getSmtpMailer();
if ($mailer === null) {
return response()->json(['success' => false, 'message' => 'Impossibile creare il mailer SMTP. Verifica la configurazione.']);
}
$encryption = $settings->smtp_encryption ?? 'tls';
$scheme = ($encryption === 'ssl') ? 'smtps' : 'smtp';
$dsn = sprintf(
'%s://%s:%s@%s:%d?encryption=%s',
$scheme,
rawurlencode($smtpUsername),
rawurlencode($smtpPassword),
$settings->smtp_host,
$settings->smtp_port ?? 587,
$encryption
);
$transport = \Symfony\Component\Mailer\Transport::fromDsn($dsn);
$mailer = new \Symfony\Component\Mailer\Mailer($transport);
$fromName = $settings->email_name ?? '';
if (filter_var($fromName, FILTER_VALIDATE_EMAIL)) {
$fromName = 'Glastree';
@@ -182,7 +146,7 @@ class EmailSettingsController extends Controller
->to($request->email)
->subject('Test Configurazione SMTP - Glastree')
->text('Test email da Glastree - Configurazione SMTP funziona!');
$mailer->send($email);
\Illuminate\Support\Facades\Log::info('Test SMTP ok', ['to' => $request->email, 'from' => $settings->email_address]);
@@ -227,6 +191,8 @@ class EmailSettingsController extends Controller
'verify_email' => 'nullable|email|max:255',
'note' => 'nullable|string|max:500',
'is_active' => 'boolean',
'auth_method' => 'nullable|in:password,oauth',
'google_oauth_connection_id' => 'nullable|integer|exists:google_oauth_connections,id',
]);
if (!empty($data['smtp_password'])) {
@@ -257,6 +223,8 @@ class EmailSettingsController extends Controller
'verify_email' => 'nullable|email|max:255',
'note' => 'nullable|string|max:500',
'is_active' => 'boolean',
'auth_method' => 'nullable|in:password,oauth',
'google_oauth_connection_id' => 'nullable|integer|exists:google_oauth_connections,id',
]);
if (!empty($data['smtp_password'])) {
@@ -326,4 +294,4 @@ class EmailSettingsController extends Controller
return response()->json(['success' => false, 'message' => 'Errore invio: ' . $e->getMessage()]);
}
}
}
}
+21 -9
View File
@@ -56,7 +56,7 @@ class DocumentoController extends Controller
} elseif ($folderId) {
$query->where('cartella_id', $folderId);
} else {
$query->whereNull('repository_id');
$query->whereNull('repository_id')->whereNull('cartella_id');
}
$documenti = $query->paginate(20);
@@ -204,24 +204,24 @@ class DocumentoController extends Controller
$data = $request->validate([
'nome_file' => 'required|string|max:255',
'tipologia' => 'required|' . $tipologieRule,
'visibilita' => 'nullable|string',
'contesto_tipo' => 'nullable|in:individuo,gruppo,evento,mailing',
'visibilita_target_id' => 'nullable|integer',
'visibilita_target_type' => 'nullable|string',
'note' => 'nullable|string',
'tags' => 'nullable|array',
'tags.*' => 'exists:tags,id',
]);
$data['visibilita'] = $request->contesto_tipo ?: 'pubblico';
$contestoTipo = $data['contesto_tipo'] ?? null;
$data['visibilita'] = $contestoTipo ?: 'pubblico';
if ($request->contesto_tipo) {
if ($contestoTipo) {
$typeMap = [
'individuo' => Individuo::class,
'gruppo' => Gruppo::class,
'evento' => Evento::class,
'mailing' => \App\Models\MailingList::class,
];
$data['visibilita_target_type'] = $typeMap[$request->contesto_tipo] ?? null;
$data['visibilita_target_type'] = $typeMap[$contestoTipo] ?? null;
} else {
$data['visibilita'] = 'pubblico';
$data['visibilita_target_id'] = null;
@@ -236,21 +236,27 @@ class DocumentoController extends Controller
$documento->tags()->detach();
}
return redirect('/documenti')->with('success', 'Documento aggiornato.');
$redirect = $request->input('folder_id') ? '/documenti?folder_id=' . $request->input('folder_id') : '/documenti';
return redirect($redirect)->with('success', 'Documento aggiornato.');
}
public function store(Request $request)
{
$this->authorizeWrite('documenti');
$tipologieValidi = TipologiaDocumento::opzioni();
$tipologieRule = 'in:' . implode(',', $tipologieValidi);
$data = $request->validate([
'nome_file' => 'required|string|max:255',
'tipologia' => 'required|in:avatar,galleria,documento,statuto,altro',
'tipologia' => 'required|' . $tipologieRule,
'file' => 'required|file|max:10240',
'visibilita' => 'required|in:pubblico,individuo,gruppo',
'visibilita_target_id' => 'nullable|integer',
'visibilita_target_type' => 'nullable|string',
'cartella_id' => 'nullable|integer|exists:documenti_cartelle,id',
'repository_id' => 'nullable|integer|exists:storage_repositories,id',
'tags' => 'nullable|array',
'tags.*' => 'exists:tags,id',
]);
$repositoryId = $data['repository_id'] ?? null;
@@ -284,7 +290,7 @@ class DocumentoController extends Controller
$visibilitaTargetType = Gruppo::class;
}
Documento::create([
$doc = Documento::create([
'nome_file' => $data['nome_file'],
'file_path' => $filePath,
'storage_disk' => $disk ?? null,
@@ -300,7 +306,13 @@ class DocumentoController extends Controller
'repository_id' => $repositoryId,
]);
if ($doc && $request->has('tags')) {
$doc->tags()->sync($request->tags);
}
$redirect = $request->_redirect ?? url()->previous();
if ($request->filled('folder_id')) {
$redirect = '/documenti?folder_id=' . $request->input('folder_id');
}
return redirect($redirect)->with('success', 'Documento caricato.');
}
+37
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Evento;
@@ -630,4 +632,39 @@ class EventoController extends Controller
return redirect('/eventi')->with('success', $message);
}
public function massTag(Request $request)
{
$this->authorizeWrite('eventi');
$idsInput = $request->input('ids', '');
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
if (empty($ids)) {
return back()->with('error', 'Nessun evento selezionato.');
}
$data = $request->validate([
'tags' => 'required|array',
'tags.*' => 'exists:tags,id',
'mode' => 'required|in:assign,remove',
]);
$tagIds = $data['tags'];
$mode = $data['mode'];
$count = 0;
Evento::whereIn('id', $ids)->chunk(100, function ($eventi) use ($tagIds, $mode, &$count) {
foreach ($eventi as $evento) {
if ($mode === 'assign') {
$evento->tags()->syncWithoutDetaching($tagIds);
} else {
$evento->tags()->detach($tagIds);
}
$count++;
}
});
$actionLabel = $mode === 'assign' ? 'assegnati' : 'rimossi';
return back()->with('success', "Tag $actionLabel per $count eventi.");
}
}
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Evento;
@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Enums\GoogleService;
use App\Models\GoogleOAuthConnection;
use App\Services\GoogleOAuthService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
class GoogleOAuthController extends Controller
{
public function __construct(
private readonly GoogleOAuthService $googleOAuthService,
) {}
public function redirect(string $service): RedirectResponse
{
try {
$serviceEnum = GoogleService::from($service);
} catch (\ValueError $e) {
return redirect()->back()->withErrors(['error' => 'Servizio Google non valido: ' . $service]);
}
$authUrl = $this->googleOAuthService->getAuthUrl($service);
return redirect()->away($authUrl);
}
public function callback(Request $request): RedirectResponse
{
$error = $request->input('error');
if ($error) {
$message = match ($error) {
'access_denied' => 'Accesso negato. Autorizzazione non concessa.',
default => 'Errore Google: ' . $error,
};
return redirect()->route('impostazioni.index', ['tab' => 'google'])
->withErrors(['error' => $message]);
}
$code = $request->input('code');
if (!$code) {
return redirect()->route('impostazioni.index', ['tab' => 'google'])
->withErrors(['error' => 'Nessun codice di autorizzazione ricevuto']);
}
$state = $request->input('state', 'email');
try {
$service = GoogleService::from($state);
} catch (\ValueError $e) {
$service = GoogleService::Email;
}
try {
$connection = $this->googleOAuthService->handleCallback($code, $service);
$message = match ($service) {
GoogleService::Email => 'Account email Gmail connesso con successo!',
GoogleService::Drive => 'Account Google Drive connesso con successo!',
GoogleService::Calendar => 'Account Google Calendar connesso con successo!',
};
return redirect()->route('impostazioni.index', ['tab' => 'google'])
->with('success', $message);
} catch (\Exception $e) {
Log::error('Google OAuth callback error', [
'error' => $e->getMessage(),
'service' => $service->value,
]);
return redirect()->route('impostazioni.index', ['tab' => 'google'])
->withErrors(['error' => 'Errore durante la connessione: ' . $e->getMessage()]);
}
}
public function revoke(GoogleOAuthConnection $connection): RedirectResponse
{
if ($connection->user_id !== auth()->id()) {
abort(403);
}
try {
$service = GoogleService::from($connection->service);
$message = match ($service) {
GoogleService::Email => 'Account email Gmail disconnesso.',
GoogleService::Drive => 'Account Google Drive disconnesso.',
GoogleService::Calendar => 'Account Google Calendar disconnesso.',
};
$this->googleOAuthService->revoke($connection);
return redirect()->route('impostazioni.index', ['tab' => 'google'])
->with('success', $message);
} catch (\Exception $e) {
Log::error('Google OAuth revoke error', [
'error' => $e->getMessage(),
'connection_id' => $connection->id,
]);
return redirect()->route('impostazioni.index', ['tab' => 'google'])
->withErrors(['error' => 'Errore durante la disconnessione: ' . $e->getMessage()]);
}
}
public function status(): \Illuminate\Http\JsonResponse
{
$status = $this->googleOAuthService->getConnectionStatus();
return response()->json($status);
}
}
@@ -10,6 +10,7 @@ use App\Models\Ruolo;
use App\Models\SenderAccount;
use App\Models\StorageRepository;
use App\Models\Tag;
use App\Services\GoogleOAuthService;
use App\Models\TipologiaDocumento;
use App\Models\TipologiaEvento;
use Illuminate\Http\RedirectResponse;
@@ -34,8 +35,9 @@ class ImpostazioniController extends Controller
$repositories = StorageRepository::orderBy('ordine')->get();
$googleDriveNewToken = session('google_drive_new_token');
$calendarioConnessioni = CalendarioConnessione::orderBy('ordine')->get();
$googleStatus = app(GoogleOAuthService::class)->getConnectionStatus();
return view('impostazioni.index', compact('tipologie', 'tipologieEventi', 'ruoli', 'tags', 'appSettings', 'emailSettings', 'senderAccounts', 'repositories', 'googleDriveNewToken', 'calendarioConnessioni'));
return view('impostazioni.index', compact('tipologie', 'tipologieEventi', 'ruoli', 'tags', 'appSettings', 'emailSettings', 'senderAccounts', 'repositories', 'googleDriveNewToken', 'calendarioConnessioni', 'googleStatus'));
}
public function saveAppSettings(Request $request)