++ 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>
This commit is contained in:
@@ -13,11 +13,8 @@ class LoginController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): RedirectResponse
|
||||
{
|
||||
// Il campo si chiama "email" ma accetta anche il nome utente del
|
||||
// sorvegliante di gruppo, che l'email non ce l'ha: con la chiocciola è
|
||||
// un'email, senza un nome utente (che la chiocciola non la contiene mai).
|
||||
$credentials = $request->validate([
|
||||
'email' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required', 'string', 'min:6'],
|
||||
'remember' => ['nullable', 'boolean'],
|
||||
]);
|
||||
@@ -32,11 +29,8 @@ class LoginController extends Controller
|
||||
->withInput($request->only('email', 'remember'));
|
||||
}
|
||||
|
||||
$identificativo = trim($credentials['email']);
|
||||
$campo = str_contains($identificativo, '@') ? 'email' : 'username';
|
||||
|
||||
if (! Auth::attempt([
|
||||
$campo => $campo === 'username' ? Str::lower($identificativo) : $identificativo,
|
||||
'email' => $credentials['email'],
|
||||
'password' => $credentials['password'],
|
||||
], $request->boolean('remember'))) {
|
||||
RateLimiter::hit($throttleKey);
|
||||
@@ -46,16 +40,6 @@ class LoginController extends Controller
|
||||
->withInput($request->only('email', 'remember'));
|
||||
}
|
||||
|
||||
// Utenza disattivata (gruppo di servizio sciolto): la password è giusta
|
||||
// ma non si entra.
|
||||
if (Auth::user()->isDisattivato()) {
|
||||
Auth::logout();
|
||||
|
||||
return back()
|
||||
->withErrors(['email' => 'Questa utenza è stata disattivata.'])
|
||||
->withInput($request->only('email', 'remember'));
|
||||
}
|
||||
|
||||
RateLimiter::clear($throttleKey);
|
||||
$request->session()->regenerate();
|
||||
activity()->causedBy(auth()->user())->log('login');
|
||||
|
||||
@@ -4,23 +4,18 @@ namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Due regole per gli utenti autenticati.
|
||||
*
|
||||
* 1. Un'utenza disattivata (gruppo di servizio sciolto) non può più lavorare:
|
||||
* anche una sessione già aperta viene chiusa alla richiesta successiva.
|
||||
*
|
||||
* 2. Il sorvegliante di gruppo vede solo il proprio gruppo e il proprio
|
||||
* profilo. La Home lo porta a "Il mio gruppo"; ogni altra pagina è 403,
|
||||
* anche scrivendone l'indirizzo a mano. Le azioni Livewire passano: i
|
||||
* componenti che può aprire controllano da sé che territori e assegnazioni
|
||||
* siano del suo gruppo, e gli altri non può caricarli.
|
||||
* Il sorvegliante di gruppo vede solo il proprio gruppo e il proprio
|
||||
* profilo. La Home lo porta a "Il mio gruppo"; ogni altra pagina è 403,
|
||||
* anche scrivendone l'indirizzo a mano. Le azioni Livewire passano: i
|
||||
* componenti che può aprire controllano da sé che territori e assegnazioni
|
||||
* siano del suo gruppo, e gli altri non può caricarli.
|
||||
*
|
||||
* Vale solo per chi ha il ruolo sorvegliante e nessun altro: un utente con
|
||||
* anche un altro ruolo resta regolato dai permessi di quel ruolo.
|
||||
* anche un altro ruolo resta regolato dai permessi di quel ruolo. Sciolto il
|
||||
* gruppo l'utenza si elimina: una sessione ancora aperta non ha più utente.
|
||||
*/
|
||||
class LimitaSorvegliante
|
||||
{
|
||||
@@ -43,14 +38,6 @@ class LimitaSorvegliante
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($user->isDisattivato()) {
|
||||
Auth::guard('web')->logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect()->route('login')->withErrors(['email' => 'Questa utenza è stata disattivata.']);
|
||||
}
|
||||
|
||||
if (! $user->isSorvegliante() || $user->roles->count() > 1) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ use Livewire\Component;
|
||||
|
||||
/**
|
||||
* Gruppi di servizio (solo amministratore): elenco e creazione. Creando un
|
||||
* gruppo si sceglie il sorvegliante, che riceve un'utenza con un nome utente
|
||||
* ricavato dal nome del gruppo (niente email) e una password temporanea,
|
||||
* mostrati qui una volta sola. Membri e pacchetto si gestiscono nella pagina
|
||||
* del gruppo (GruppoShow).
|
||||
* gruppo si sceglie il sorvegliante, che riceve un'utenza con un'email
|
||||
* fittizia ricavata dal suo nome (GruppiServizio::emailFittizia) e una
|
||||
* password temporanea, mostrate qui una volta sola. Membri e pacchetto si
|
||||
* gestiscono nella pagina del gruppo (GruppoShow).
|
||||
*/
|
||||
class GruppiIndex extends Component
|
||||
{
|
||||
@@ -72,11 +72,11 @@ class GruppiIndex extends Component
|
||||
->orderBy('nome')
|
||||
->get();
|
||||
|
||||
// Possono diventare sorveglianti i proclamatori attivi senza gruppo e
|
||||
// senza un'utenza attiva (quella di un gruppo sciolto si riattiva).
|
||||
// Possono diventare sorveglianti i proclamatori attivi senza gruppo (e
|
||||
// quindi senza utenza: sciolto il gruppo, la si elimina).
|
||||
$candidati = Proclamatore::attivi()
|
||||
->whereNull('gruppo_servizio_id')
|
||||
->whereDoesntHave('utente', fn ($q) => $q->whereNull('disattivato_at'))
|
||||
->whereDoesntHave('utente')
|
||||
->get();
|
||||
|
||||
return view('livewire.gruppi.gruppi-index', [
|
||||
|
||||
@@ -124,7 +124,7 @@ class GruppoShow extends Component
|
||||
$nome = $this->gruppo->nome;
|
||||
$this->servizio()->sciogli($this->gruppo);
|
||||
|
||||
session()->flash('success', "Gruppo {$nome} sciolto: i territori non ancora dati sono tornati in reparto e l'utenza del sorvegliante è disattivata.");
|
||||
session()->flash('success', "Gruppo {$nome} sciolto: i territori non ancora dati sono tornati in reparto e l'utenza del sorvegliante è stata eliminata.");
|
||||
return $this->redirect(route('gruppi.index'), navigate: true);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ class ProclamatoreIndex extends Component
|
||||
/** L'avviso quando un sorvegliante esce di scena: anche per ProclamatoreEdit. */
|
||||
public static function gruppoSciolto(GruppoServizio $gruppo): string
|
||||
{
|
||||
return "Era il sorvegliante del gruppo {$gruppo->nome}: il gruppo è stato sciolto, i territori non ancora dati sono tornati in reparto e la sua utenza è disattivata.";
|
||||
return "Era il sorvegliante del gruppo {$gruppo->nome}: il gruppo è stato sciolto, i territori non ancora dati sono tornati in reparto e la sua utenza è stata eliminata.";
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -20,24 +20,24 @@ class Profile extends Component
|
||||
{
|
||||
$user = auth()->user();
|
||||
$this->name = $user->name;
|
||||
$this->email = (string) $user->email;
|
||||
$this->email = $user->email;
|
||||
}
|
||||
|
||||
public function updateProfile()
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
// Il sorvegliante di gruppo entra con il nome utente: l'email non gli
|
||||
// si chiede, e il nome utente lo decide il nome del gruppo.
|
||||
$senzaEmail = $user->isSorvegliante();
|
||||
// Il sorvegliante di gruppo entra con l'email fittizia creata col
|
||||
// gruppo (GruppiServizio::emailFittizia): dal profilo non la cambia.
|
||||
$emailFissa = $user->isSorvegliante();
|
||||
|
||||
$validated = $this->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
] + ($senzaEmail ? [] : [
|
||||
] + ($emailFissa ? [] : [
|
||||
'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')->ignore($user->id)],
|
||||
]));
|
||||
|
||||
$user->fill(['name' => $validated['name']] + ($senzaEmail ? [] : ['email' => $validated['email']]));
|
||||
$user->fill(['name' => $validated['name']] + ($emailFissa ? [] : ['email' => $validated['email']]));
|
||||
$emailCambiata = $user->isDirty('email');
|
||||
$user->save();
|
||||
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
namespace App\Livewire\Settings;
|
||||
|
||||
use App\Livewire\Concerns\Avvisa;
|
||||
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
|
||||
@@ -115,7 +112,7 @@ class UsersIndex extends Component
|
||||
}
|
||||
|
||||
/**
|
||||
* Le utenze dei sorveglianti di gruppo (attive o disattivate) si gestiscono
|
||||
* Le utenze dei sorveglianti di gruppo si gestiscono
|
||||
* da Gruppi di servizio: cambiarle o cancellarle da qui lascerebbe un
|
||||
* gruppo senza sorvegliante, o un sorvegliante con un ruolo diverso.
|
||||
*/
|
||||
@@ -232,37 +229,7 @@ class UsersIndex extends Component
|
||||
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();
|
||||
});
|
||||
$user->eliminaConservandoStorico($currentUser, $user->name, $user->email);
|
||||
|
||||
if ($this->editingUserId === $userId) {
|
||||
$this->cancelEdit();
|
||||
|
||||
@@ -525,18 +525,22 @@ class XmlExchange extends Component
|
||||
$utentiDaCollegare = [];
|
||||
foreach (($xml->users->user ?? []) as $userNode) {
|
||||
$email = trim((string) ($userNode->email ?? ''));
|
||||
$username = trim((string) ($userNode->username ?? ''));
|
||||
$legacyId = (string) ($userNode['legacy_id'] ?? '');
|
||||
|
||||
// Il sorvegliante di gruppo non ha email: si riconosce dal nome utente.
|
||||
if ($email === '' && $username === '') {
|
||||
$this->pushImportIssue('utente', $legacyId, 'Email e nome utente mancanti');
|
||||
// Nei file del 19-20 ottobre 2026 c'erano le utenze disattivate
|
||||
// degli ex sorveglianti: ora, sciolto il gruppo, l'utenza non
|
||||
// esiste più. Le loro assegnazioni passano a chi importa.
|
||||
if (trim((string) ($userNode->disattivato_at ?? '')) !== '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = User::query()->firstOrNew($email !== '' ? ['email' => $email] : ['username' => $username]);
|
||||
$user->name = (string) ($userNode->name ?? ($email !== '' ? $email : $username));
|
||||
$user->username = $username !== '' ? $username : null;
|
||||
if ($email === '') {
|
||||
$this->pushImportIssue('utente', $legacyId, 'Email mancante');
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = User::query()->firstOrNew(['email' => $email]);
|
||||
$user->name = (string) ($userNode->name ?? $email);
|
||||
if (! $user->exists) {
|
||||
// password is NOT NULL; the real hash is written raw below.
|
||||
$user->password = Str::password(24);
|
||||
@@ -597,10 +601,7 @@ class XmlExchange extends Component
|
||||
|
||||
// Utenza di un sorvegliante: si collega al proclamatore dopo che
|
||||
// i proclamatori sono stati importati (vedi i gruppi di servizio).
|
||||
$utentiDaCollegare[$user->id] = [
|
||||
'proclamatore' => (string) ($userNode->legacy_proclamatore_id ?? ''),
|
||||
'disattivato_at' => $this->normalizeDateTimeForDb((string) ($userNode->disattivato_at ?? '')),
|
||||
];
|
||||
$utentiDaCollegare[$user->id] = (string) ($userNode->legacy_proclamatore_id ?? '');
|
||||
|
||||
$stats['utenti_importati']++;
|
||||
if ($legacyId !== '') {
|
||||
@@ -681,10 +682,9 @@ class XmlExchange extends Component
|
||||
DB::table('proclamatori')->where('id', $idProclamatore)->update(['gruppo_servizio_id' => $gruppoMap[$legacyGruppo]]);
|
||||
}
|
||||
}
|
||||
foreach ($utentiDaCollegare as $idUtente => $collegamento) {
|
||||
foreach ($utentiDaCollegare as $idUtente => $legacyProclamatore) {
|
||||
DB::table('users')->where('id', $idUtente)->update([
|
||||
'proclamatore_id' => $proclamatoreMap[$collegamento['proclamatore']] ?? null,
|
||||
'disattivato_at' => $collegamento['disattivato_at'],
|
||||
'proclamatore_id' => $proclamatoreMap[$legacyProclamatore] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -869,15 +869,13 @@ class XmlExchange extends Component
|
||||
'legacy_id' => (string) $u->id,
|
||||
'name' => (string) $u->name,
|
||||
'email' => (string) $u->email,
|
||||
'username' => (string) $u->username,
|
||||
'password' => (string) $u->getAuthPassword(),
|
||||
'email_verified_at' => optional($u->email_verified_at)->toIso8601String() ?? '',
|
||||
// remember_token no: all'import si azzera comunque, e nel file
|
||||
// sarebbe solo una credenziale in più per chi lo tiene.
|
||||
'roles' => $u->roles->pluck('name')->all(),
|
||||
// Sorvegliante di gruppo: il proclamatore collegato e se l'utenza è disattivata.
|
||||
// Sorvegliante di gruppo: il proclamatore collegato.
|
||||
'proclamatore_id' => $u->proclamatore_id ? (string) $u->proclamatore_id : '',
|
||||
'disattivato_at' => optional($u->disattivato_at)->toIso8601String() ?? '',
|
||||
'created_at' => optional($u->created_at)->toIso8601String() ?? '',
|
||||
'updated_at' => optional($u->updated_at)->toIso8601String() ?? '',
|
||||
])->all(),
|
||||
@@ -1285,11 +1283,9 @@ class XmlExchange extends Component
|
||||
}
|
||||
$this->addXmlText($node, 'name', (string) ($user['name'] ?? ''));
|
||||
$this->addXmlText($node, 'email', (string) ($user['email'] ?? ''));
|
||||
$this->addXmlText($node, 'username', (string) ($user['username'] ?? ''));
|
||||
$this->addXmlText($node, 'password', (string) ($user['password'] ?? ''));
|
||||
$node->addChild('email_verified_at', (string) ($user['email_verified_at'] ?? ''));
|
||||
$node->addChild('legacy_proclamatore_id', (string) ($user['proclamatore_id'] ?? ''));
|
||||
$node->addChild('disattivato_at', (string) ($user['disattivato_at'] ?? ''));
|
||||
$node->addChild('created_at', (string) ($user['created_at'] ?? ''));
|
||||
$node->addChild('updated_at', (string) ($user['updated_at'] ?? ''));
|
||||
$rolesNode = $node->addChild('roles');
|
||||
|
||||
@@ -175,22 +175,9 @@ class Proclamatore extends Model
|
||||
$this->cognome = 'Proclamatore #' . $this->id;
|
||||
$this->attivo = false;
|
||||
$this->gruppo_servizio_id = null;
|
||||
// Se era sorvegliante, il salvataggio (non più attivo) scioglie il
|
||||
// gruppo ed elimina la sua utenza: booted().
|
||||
$this->save();
|
||||
$this->delete(); // soft delete
|
||||
|
||||
// L'utenza di un ex sorvegliante (già disattivata: il sorvegliante di
|
||||
// un gruppo attivo non si anonimizza) porta nome ed email in chiaro.
|
||||
$utente = $this->utente()->first();
|
||||
if ($utente) {
|
||||
$utente->syncRoles([]);
|
||||
$utente->forceFill([
|
||||
'name' => 'Anonimo',
|
||||
'email' => null,
|
||||
'username' => "anonimo.{$utente->id}",
|
||||
'password' => \Illuminate\Support\Str::random(40),
|
||||
'remember_token' => null,
|
||||
'disattivato_at' => $utente->disattivato_at ?? now(),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ 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
|
||||
@@ -22,11 +24,9 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'username',
|
||||
'password',
|
||||
'password_change_required',
|
||||
'proclamatore_id',
|
||||
'disattivato_at',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
@@ -40,7 +40,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'password_change_required' => 'boolean',
|
||||
'disattivato_at' => 'datetime',
|
||||
'proclamatore_id' => 'integer',
|
||||
];
|
||||
}
|
||||
@@ -60,7 +59,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return $this->hasRole('sorvegliante');
|
||||
}
|
||||
|
||||
/** Il gruppo di cui è sorvegliante, se c'è (sciolto, il gruppo non esiste più). */
|
||||
/** Il gruppo di cui è sorvegliante (sciolto il gruppo, l'utenza non esiste più). */
|
||||
public function gruppoSorvegliato(): ?GruppoServizio
|
||||
{
|
||||
return $this->proclamatore_id
|
||||
@@ -69,18 +68,37 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
}
|
||||
|
||||
/**
|
||||
* Con cosa entra: l'email, o il nome utente per il sorvegliante di
|
||||
* gruppo, che l'email non ce l'ha. Da mostrare dove prima c'era l'email.
|
||||
* 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 getIdentificativoAttribute(): string
|
||||
public function eliminaConservandoStorico(self $subentra, string $nome, ?string $email): void
|
||||
{
|
||||
return (string) ($this->email ?? $this->username ?? $this->name);
|
||||
}
|
||||
DB::transaction(function () use ($subentra, $nome, $email) {
|
||||
$eliminatoIl = now()->toDateTimeString();
|
||||
|
||||
/** Utenza disattivata (gruppo sciolto): non può più entrare. */
|
||||
public function isDisattivato(): bool
|
||||
{
|
||||
return $this->disattivato_at !== null;
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,12 +21,15 @@ use InvalidArgumentException;
|
||||
*/
|
||||
class GruppiServizio
|
||||
{
|
||||
/** Il dominio delle email fittizie dei sorveglianti (iniziale+cognome@…). */
|
||||
public const DOMINIO_EMAIL = 'termanager.it';
|
||||
|
||||
/**
|
||||
* Crea il gruppo e l'utenza del sorvegliante: nome utente ricavato dal
|
||||
* nome del gruppo (niente email), ruolo "sorvegliante", password
|
||||
* temporanea da cambiare al primo accesso. Restituisce il gruppo e la
|
||||
* password temporanea, da mostrare una volta sola all'amministratore
|
||||
* insieme al nome utente ($gruppo->sorvegliante->utente->username).
|
||||
* Crea il gruppo e l'utenza del sorvegliante: email fittizia ricavata dal
|
||||
* suo nome (emailFittizia), ruolo "sorvegliante", password temporanea da
|
||||
* cambiare al primo accesso, nessuna conferma dell'email. Restituisce il
|
||||
* gruppo e la password temporanea, da mostrare una volta sola
|
||||
* all'amministratore insieme all'email.
|
||||
*
|
||||
* @param int[] $membri id dei proclamatori, oltre al sorvegliante
|
||||
* @return array{0: GruppoServizio, 1: string}
|
||||
@@ -46,29 +49,25 @@ class GruppiServizio
|
||||
if ($sorvegliante->gruppo_servizio_id) {
|
||||
throw new InvalidArgumentException("{$sorvegliante->nome_completo} fa già parte di un altro gruppo.");
|
||||
}
|
||||
// Chi è stato sorvegliante di un gruppo sciolto ha già un'utenza,
|
||||
// disattivata: si riattiva quella (un proclamatore, un'utenza).
|
||||
$esistente = $sorvegliante->utente()->first();
|
||||
if ($esistente && ! $esistente->isDisattivato()) {
|
||||
if ($sorvegliante->utente()->exists()) {
|
||||
throw new InvalidArgumentException("{$sorvegliante->nome_completo} ha già un'utenza.");
|
||||
}
|
||||
|
||||
$password = Str::password(12, symbols: false);
|
||||
|
||||
$gruppo = DB::transaction(function () use ($nome, $sorvegliante, $membri, $password, $esistente) {
|
||||
$gruppo = DB::transaction(function () use ($nome, $sorvegliante, $membri, $password) {
|
||||
$gruppo = GruppoServizio::create(['nome' => $nome, 'sorvegliante_id' => $sorvegliante->id]);
|
||||
$sorvegliante->update(['gruppo_servizio_id' => $gruppo->id]);
|
||||
$this->aggiungiMembri($gruppo, $membri);
|
||||
|
||||
$utente = $esistente ?? new User(['proclamatore_id' => $sorvegliante->id]);
|
||||
$utente->forceFill([
|
||||
$utente = User::create([
|
||||
'name' => $sorvegliante->nome_completo,
|
||||
'username' => self::nomeUtente($nome),
|
||||
'email' => self::emailFittizia($sorvegliante),
|
||||
'password' => $password,
|
||||
'password_change_required' => true,
|
||||
'disattivato_at' => null,
|
||||
])->save();
|
||||
$utente->syncRoles(['sorvegliante']);
|
||||
'proclamatore_id' => $sorvegliante->id,
|
||||
]);
|
||||
$utente->assignRole('sorvegliante');
|
||||
|
||||
activity()->causedBy(auth()->user())->performedOn($gruppo)
|
||||
->withProperties(['sorvegliante_id' => $sorvegliante->id, 'utente_id' => $utente->id])
|
||||
@@ -81,20 +80,27 @@ class GruppiServizio
|
||||
}
|
||||
|
||||
/**
|
||||
* Il nome utente dal nome del gruppo: minuscole, senza accenti, parole
|
||||
* unite da un punto ("Gruppo Noce" → "gruppo.noce"); se è già preso, un
|
||||
* numero in coda ("zisa2"). Mai una chiocciola: al login distingue il
|
||||
* nome utente dall'email.
|
||||
* L'email fittizia del sorvegliante, con cui entra: iniziale del nome e
|
||||
* cognome, minuscoli, senza accenti, spazi né apostrofi, sul dominio
|
||||
* dell'app (Francesco Fontana → ffontana@termanager.it, Francesco
|
||||
* D'Angelo → fdangelo@…). Se è già presa, un numero in coda
|
||||
* (ffontana2@…). Non riceve posta: il sorvegliante non conferma l'email.
|
||||
*/
|
||||
public static function nomeUtente(string $nomeGruppo): string
|
||||
public static function emailFittizia(Proclamatore $proclamatore): string
|
||||
{
|
||||
$base = Str::limit(Str::slug($nomeGruppo, '.') ?: 'gruppo', 50, '');
|
||||
$nome = $base;
|
||||
for ($n = 2; User::where('username', $nome)->exists(); $n++) {
|
||||
$nome = $base . $n;
|
||||
$pulisci = fn (string $testo) => preg_replace('/[^a-z0-9]/', '', Str::lower(Str::ascii($testo)));
|
||||
|
||||
$base = Str::limit(mb_substr($pulisci((string) $proclamatore->nome), 0, 1) . $pulisci((string) $proclamatore->cognome), 60, '');
|
||||
if ($base === '') {
|
||||
$base = "sorvegliante{$proclamatore->id}";
|
||||
}
|
||||
|
||||
return $nome;
|
||||
$email = "{$base}@" . self::DOMINIO_EMAIL;
|
||||
for ($n = 2; User::where('email', $email)->exists(); $n++) {
|
||||
$email = "{$base}{$n}@" . self::DOMINIO_EMAIL;
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
/** @param int[] $ids */
|
||||
@@ -250,8 +256,10 @@ class GruppiServizio
|
||||
* I territori del pacchetto non ancora dati tornano in reparto; quelli
|
||||
* già dati restano ai membri come assegnazioni qualsiasi, che conservano
|
||||
* solo il nome del gruppo (gruppo_nome). I membri restano senza gruppo.
|
||||
* L'utenza del sorvegliante si disattiva e libera il nome utente; resta
|
||||
* perché è l'autrice delle assegnazioni che ha fatto.
|
||||
* L'utenza del sorvegliante si elimina: torna un proclamatore come gli
|
||||
* altri. Le assegnazioni che aveva fatto o fatto rientrare passano a chi
|
||||
* scioglie il gruppo; nel registro le sue voci restano, a nome
|
||||
* "Sorvegliante del gruppo …", senza dati personali.
|
||||
*/
|
||||
public function sciogli(GruppoServizio $gruppo, ?string $motivo = null): void
|
||||
{
|
||||
@@ -269,30 +277,39 @@ class GruppiServizio
|
||||
Assegnazione::where('gruppo_servizio_id', $gruppo->id)->update(['gruppo_servizio_id' => null]);
|
||||
|
||||
$utente = User::where('proclamatore_id', $gruppo->sorvegliante_id)->first();
|
||||
if ($utente) {
|
||||
$utente->removeRole('sorvegliante');
|
||||
// Il nome utente del gruppo si libera; al suo posto uno che
|
||||
// identifica l'utenza (backup XML) e con cui non si entra.
|
||||
$utente->forceFill([
|
||||
'disattivato_at' => now(),
|
||||
'remember_token' => null,
|
||||
'username' => "ex.sorvegliante.{$utente->id}",
|
||||
])->save();
|
||||
}
|
||||
|
||||
activity()->causedBy(auth()->user())->performedOn($gruppo)
|
||||
->withProperties(array_filter([
|
||||
'gruppo' => $gruppo->nome,
|
||||
'territori_liberati' => $liberati->all(),
|
||||
'utente_disattivato' => $utente?->id,
|
||||
'utente_eliminato' => $utente?->id,
|
||||
'motivo' => $motivo,
|
||||
], fn ($v) => $v !== null))
|
||||
->log('gruppo_sciolto');
|
||||
|
||||
if ($utente) {
|
||||
$utente->eliminaConservandoStorico($this->subentrante($utente), "Sorvegliante del gruppo {$gruppo->nome}", null);
|
||||
}
|
||||
|
||||
$gruppo->delete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Chi prende le assegnazioni fatte dal sorvegliante quando la sua utenza
|
||||
* si elimina: chi sta sciogliendo il gruppo, altrimenti il primo
|
||||
* amministratore.
|
||||
*/
|
||||
private function subentrante(User $sorvegliante): User
|
||||
{
|
||||
$chi = auth()->user();
|
||||
if ($chi instanceof User && $chi->id !== $sorvegliante->id) {
|
||||
return $chi;
|
||||
}
|
||||
|
||||
return User::role('amministratore')->orderBy('id')->firstOrFail();
|
||||
}
|
||||
|
||||
private function attivo(GruppoServizio $gruppo): void
|
||||
{
|
||||
if (! $gruppo->exists || ! GruppoServizio::whereKey($gruppo->id)->exists()) {
|
||||
|
||||
Reference in New Issue
Block a user