Files
termanager2/app/Livewire/Settings/UsersIndex.php
Francesco Picone d433c9392d S-13 sul modulo ufficiale e correzioni all'integrità dei dati
Modulo S-13
- Il PDF scrive sopra il modulo ufficiale (FPDI, matrice convertita in PDF 1.4)
  invece di ricostruirlo in HTML: 20 righe per foglio come l'originale.
- Una riga per ogni territorio attivo; riporto dell'ultimo rientro prima
  dell'anno; assegnazioni a cavallo d'anno su entrambi i fogli; scelta per
  data e non per anno registrato; nomi come "Cognome I.".
- Logica spostata in App\Services\Registro\ModuloS13.

Integrità dei dati
- Export/import XML (e quindi backup/ripristino): le date non slittano più di
  un giorno; i file vecchi in UTC tornano al giorno giusto.
- Import XML: rifiuta file estranei o incompleti senza toccare i dati, errore
  con riga e motivo, territori senza zona importati con il loro storico,
  caratteri di controllo rimossi in export, chi importa conserva password e
  ruolo di amministratore.
- Assegnazioni: assegnazioneCorrente corretta (filtro dentro ofMany), periodi
  sovrapposti rifiutati in Assegna e Registro sotto lock, anno di servizio
  ricavato dalla data, niente date future nel Registro.
- Utenti: non si declassa sé stessi né l'ultimo amministratore.
- Entrypoint: non sovrascrive più la copia persistente di APP_KEY con un
  valore diverso; serve APP_KEY_ROTATE=1 per un cambio voluto.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:35:51 +02:00

214 lines
6.9 KiB
PHP

<?php
namespace App\Livewire\Settings;
use App\Models\Assegnazione;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;
use Spatie\Activitylog\Models\Activity;
use Spatie\Permission\Models\Role;
class UsersIndex extends Component
{
public string $name = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
public string $selectedRole = '';
// Locked: è la lista che le regole di validazione usano per accettare un
// ruolo; modificabile dal client, ammetterebbe qualsiasi nome.
#[Locked]
public array $availableRoles = [];
public ?int $editingUserId = null;
public string $editName = '';
public string $editEmail = '';
public string $editPassword = '';
public string $editPassword_confirmation = '';
public string $editSelectedRole = '';
public function mount(): void
{
$this->availableRoles = Role::query()
->orderBy('name')
->pluck('name')
->all();
if (! empty($this->availableRoles)) {
$this->selectedRole = $this->availableRoles[0];
}
}
protected function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'selectedRole' => ['required', 'string', Rule::in($this->availableRoles)],
];
}
protected function editRules(): array
{
return [
'editName' => ['required', 'string', 'max:255'],
'editEmail' => [
'required',
'email',
'max:255',
Rule::unique('users', 'email')->ignore($this->editingUserId),
],
'editPassword' => ['nullable', 'string', 'min:8', 'confirmed'],
'editSelectedRole' => ['required', 'string', Rule::in($this->availableRoles)],
];
}
public function createUser(): void
{
$validated = $this->validate();
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => $validated['password'],
]);
$user->syncRoles([$validated['selectedRole']]);
$this->reset(['name', 'email', 'password', 'password_confirmation']);
if (! empty($this->availableRoles)) {
$this->selectedRole = $this->availableRoles[0];
}
session()->flash('success', 'Utente creato con successo.');
}
public function startEdit(int $userId): void
{
$user = User::query()->with('roles')->findOrFail($userId);
$this->editingUserId = $user->id;
$this->editName = $user->name;
$this->editEmail = $user->email;
$this->editPassword = '';
$this->editPassword_confirmation = '';
$this->editSelectedRole = $user->roles->first()?->name ?? ($this->availableRoles[0] ?? '');
}
public function cancelEdit(): void
{
$this->reset([
'editingUserId',
'editName',
'editEmail',
'editPassword',
'editPassword_confirmation',
'editSelectedRole',
]);
}
public function updateUser(): void
{
if (! $this->editingUserId) {
return;
}
$validated = $this->validate($this->editRules());
$user = User::query()->findOrFail($this->editingUserId);
// Togliere il ruolo di amministratore a sé stessi, o all'ultimo
// rimasto, lascia l'installazione senza nessuno che possa gestire
// utenti, impostazioni e backup.
if ($user->hasRole('amministratore') && $validated['editSelectedRole'] !== 'amministratore') {
if ($user->id === auth()->id()) {
$this->addError('editSelectedRole', 'Non puoi togliere a te stesso il ruolo di amministratore: chiedilo a un altro amministratore.');
return;
}
if (User::role('amministratore')->count() <= 1) {
$this->addError('editSelectedRole', "È l'ultimo amministratore: assegna prima il ruolo a un altro utente.");
return;
}
}
$user->name = $validated['editName'];
$user->email = $validated['editEmail'];
if (! empty($validated['editPassword'])) {
$user->password = $validated['editPassword'];
}
$user->save();
$user->syncRoles([$validated['editSelectedRole']]);
$this->cancelEdit();
session()->flash('success', 'Utente aggiornato con successo.');
}
public function deleteUser(int $userId): void
{
$currentUser = auth()->user();
$user = User::query()->with('roles')->findOrFail($userId);
if (! $currentUser || $currentUser->id === $user->id) {
session()->flash('error', 'Non puoi cancellare il tuo utente.');
return;
}
if ($user->hasRole('amministratore') && User::role('amministratore')->count() <= 1) {
session()->flash('error', 'Non puoi cancellare l\'ultimo amministratore.');
return;
}
DB::transaction(function () use ($user, $currentUser) {
$causerName = $user->name;
$causerEmail = $user->email;
$deletedAt = now()->toDateTimeString();
Activity::query()
->where('causer_type', User::class)
->where('causer_id', $user->id)
->chunkById(200, function ($activities) use ($causerName, $causerEmail, $deletedAt) {
foreach ($activities as $activity) {
$properties = $activity->properties?->toArray() ?? [];
$properties['causer_name'] = $causerName;
$properties['causer_email'] = $causerEmail;
$properties['causer_deleted_at'] = $deletedAt;
$activity->properties = $properties;
$activity->save();
}
});
Assegnazione::query()
->where('created_by', $user->id)
->update(['created_by' => $currentUser->id]);
Assegnazione::query()
->where('returned_by', $user->id)
->update(['returned_by' => $currentUser->id]);
$user->syncRoles([]);
$user->delete();
});
if ($this->editingUserId === $userId) {
$this->cancelEdit();
}
session()->flash('success', 'Utente cancellato. I log sono stati preservati.');
}
public function render()
{
return view('livewire.settings.users-index', [
'users' => User::query()->with('roles')->orderBy('name')->get(),
]);
}
}