Files
termanager2/app/Models/User.php
Francesco Picone ec2e7bd7e9 ++ gruppi: email fittizia iniziale+cognome per il sorvegliante; sciolto il gruppo l'utenza si elimina
- L'utenza del sorvegliante ha un'email fittizia creata dall'app:
  iniziale del nome e cognome @termanager.it (ffontana@termanager.it),
  come gli altri utenti. Via il nome utente: login di nuovo per email.
- Sciolto il gruppo l'utenza si elimina, il sorvegliante torna un
  proclamatore come gli altri: assegnazioni a chi scioglie, registro a nome
  "Sorvegliante del gruppo ...". Via le utenze disattivate.
- User::eliminaConservandoStorico(): l'eliminazione della pagina Utenti in
  un metodo solo, usato anche dallo scioglimento.
- Migrazione 000030: elimina le utenze gia' disattivate, toglie
  users.username e users.disattivato_at, email di nuovo obbligatoria.
- XML, informativa privacy, README, HANDOFF e test aggiornati.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:09:15 +02:00

180 lines
5.8 KiB
PHP

<?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 Spatie\Permission\Traits\HasRoles;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Models\Activity;
use Illuminate\Support\Facades\DB;
/**
* MustVerifyEmail: l'account funziona solo con l'indirizzo confermato
* (middleware `verified`, VerificaEmailController). Chi cambia email — dal
* profilo o perché la cambia l'amministratore — torna da confermare.
*/
class User extends Authenticatable implements MustVerifyEmail
{
use HasFactory, Notifiable, HasRoles, LogsActivity;
protected $fillable = [
'name',
'email',
'password',
'password_change_required',
'proclamatore_id',
];
protected $hidden = [
'password',
'remember_token',
];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'password_change_required' => 'boolean',
'proclamatore_id' => 'integer',
];
}
/** Il proclamatore di cui è l'utenza (sorvegliante di gruppo). */
public function proclamatore()
{
return $this->belongsTo(Proclamatore::class, 'proclamatore_id')->withTrashed();
}
/**
* Sorvegliante di gruppo: vede solo il proprio gruppo e il proprio
* profilo (middleware LimitaSorvegliante).
*/
public function isSorvegliante(): bool
{
return $this->hasRole('sorvegliante');
}
/** Il gruppo di cui è sorvegliante (sciolto il gruppo, l'utenza non esiste più). */
public function gruppoSorvegliato(): ?GruppoServizio
{
return $this->proclamatore_id
? GruppoServizio::where('sorvegliante_id', $this->proclamatore_id)->first()
: null;
}
/**
* Elimina l'utenza senza perdere lo storico: le assegnazioni che ha fatto
* o fatto rientrare passano a $subentra (la colonna non può restare
* vuota), e nel registro delle attività le sue voci si tengono, con
* scritto chi era ($nome, $email) al posto del collegamento all'utenza.
*/
public function eliminaConservandoStorico(self $subentra, string $nome, ?string $email): void
{
DB::transaction(function () use ($subentra, $nome, $email) {
$eliminatoIl = now()->toDateTimeString();
Activity::query()
->where('causer_type', self::class)
->where('causer_id', $this->id)
->chunkById(200, function ($attivita) use ($nome, $email, $eliminatoIl) {
foreach ($attivita as $voce) {
$proprieta = $voce->properties?->toArray() ?? [];
$proprieta['causer_name'] = $nome;
$proprieta['causer_email'] = $email;
$proprieta['causer_deleted_at'] = $eliminatoIl;
$voce->properties = $proprieta;
$voce->save();
}
});
Assegnazione::query()->where('created_by', $this->id)->update(['created_by' => $subentra->id]);
Assegnazione::query()->where('returned_by', $this->id)->update(['returned_by' => $subentra->id]);
$this->syncRoles([]);
$this->delete();
});
}
/**
* Il sorvegliante non conferma l'email (scelta dell'utente, 19 ottobre
* 2026): l'utenza la crea l'amministratore con una password temporanea.
* Per tutti gli altri resta obbligatoria.
*/
public function hasVerifiedEmail(): bool
{
return ! is_null($this->email_verified_at) || $this->isSorvegliante();
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logOnly(['name', 'email'])
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
/**
* L'utenza di un sorvegliante porta nome (ed email, spesso) di un
* proclamatore: nel registro delle attività i proclamatori compaiono solo
* con l'identificativo, quindi anche qui.
*/
public function tapActivity(\Spatie\Activitylog\Contracts\Activity $activity, string $eventName): void
{
$proclamatoreId = $this->proclamatore_id ?? $this->getOriginal('proclamatore_id');
if (! $proclamatoreId) {
return;
}
$activity->properties = collect($activity->properties)->map(function ($valori) use ($proclamatoreId) {
if (! is_array($valori)) {
return $valori;
}
foreach (['name', 'email'] as $campo) {
if (array_key_exists($campo, $valori)) {
$valori[$campo] = "proclamatore #{$proclamatoreId}";
}
}
return $valori;
});
}
protected static function booted(): void
{
// Un indirizzo nuovo va confermato di nuovo, chiunque lo cambi.
static::updating(function (self $utente) {
if ($utente->isDirty('email') && ! $utente->isDirty('email_verified_at')) {
$utente->email_verified_at = null;
}
});
}
/**
* Manda l'email di conferma; false se il server di posta non risponde o
* rifiuta. Chi chiama decide cosa dire: l'utente è comunque salvato, e
* l'email si può richiedere di nuovo.
*/
public function inviaConfermaEmail(): bool
{
if ($this->isSorvegliante()) {
return true;
}
try {
$this->sendEmailVerificationNotification();
} catch (\Throwable $e) {
report($e);
return false;
}
return true;
}
}