Paillasse/portail/app/Models/Eleve.php
Adam Belkacemi 91d7cde4c1 ajoute Moodle LTI 1.3 (V2), une 6e façon de rejoindre une classe
LTI authentifie l'élève et le fait rejoindre sa classe (même code que
le code/QR existant), pas lancer un lab précis - relecture du
CLAUDE.md qui liste LTI avec les autres façons d'ajouter des élèves.
packbackbooks/lti-1p3-tool (Apache-2.0, recommandé par le CLAUDE.md),
trois ponts vers Redis/cookies/MariaDB, élève retrouvé par identité
Moodle stable pour ne jamais créer de doublon. Enregistrement d'une
plateforme/d'un déploiement par commande Artisan (opération rare,
faite à la main par Adam), jamais une page d'administration.

Corrige au passage un vrai bug préexistant trouvé en vérifiant en réel
sur le serveur : withoutMiddleware(ValidateCsrfToken::class) sur une
route ne retire jamais le vrai middleware CSRF (ValidateCsrfToken est
un alias déprécié de PreventRequestForgery, pas la classe réellement
enregistrée) - /csp-rapport avait le même défaut depuis l'étape 14,
invisible faute de surveillance. Corrigé pour les deux par la bonne
méthode (validateCsrfTokens except, dans bootstrap/app.php).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-14 13:51:47 +02:00

75 lines
2.2 KiB
PHP

<?php
namespace App\Models;
use App\Models\Concerns\GerePreferences;
use App\Models\Concerns\GereSessionUnique;
use Database\Factories\EleveFactory;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Eleve extends Model implements AuthenticatableContract
{
/** @use HasFactory<EleveFactory> */
use Authenticatable, GerePreferences, GereSessionUnique, HasFactory;
protected $fillable = ['classe_id', 'prenom', 'nom', 'identifiant', 'code_secret', 'jeton_qr', 'lti_emetteur', 'lti_sub'];
protected $hidden = ['code_secret', 'jeton_session_actif', 'jeton_qr'];
protected $casts = [
'derniere_connexion_a' => 'datetime',
'derniere_activite_a' => 'datetime',
'preferences' => 'array',
];
/**
* En ligne : une requête authentifiée dans les 3 dernières minutes
* (middleware MetAJourActivite) — pas une vraie présence temps réel.
*/
public function estEnLigne(): bool
{
return $this->derniere_activite_a !== null && $this->derniere_activite_a->gt(now()->subMinutes(3));
}
public function getAuthPasswordName(): string
{
return 'code_secret';
}
/** @return BelongsTo<Classe, $this> */
public function classe(): BelongsTo
{
return $this->belongsTo(Classe::class);
}
/** @return HasMany<LabSession, $this> */
public function labSessions(): HasMany
{
return $this->hasMany(LabSession::class);
}
/** @return HasMany<Raccourci, $this> */
public function raccourcis(): HasMany
{
return $this->hasMany(Raccourci::class)->orderBy('ordre');
}
/** @return HasOne<FilConversation, $this> */
public function filConversation(): HasOne
{
return $this->hasOne(FilConversation::class);
}
public function nomAffiche(): string
{
return $this->nom
? $this->prenom.' '.mb_substr($this->nom, 0, 1).'.'
: $this->prenom;
}
}